qt script 管理qobject的模式。
在qt的脚本环境下,gc将自动回收哪些没有ref的对象。
但是qt如何回收那些底下的c++ qobject对象,当它们的js wrapper 对象被回收时候。
qt提供的模型是你在创建一个scriptble对象时候,你可以指定3种模型。
(). 第二个参数...
了解了这个细节,有助于在开发时候,避免出现应该销毁c++ 对象时候没有;或者在访问时候发现c++对象已经销毁了。
联想到去年做v8的api wrapper layer时候,在mapping模式下当时只是支持全局型c++对象mapping过去。 就是为了避免引入ref counting 等对象生命期维护问题。
1. qt 控制型
script enviroment 不管理, 完全由qt 的object tree mode管理。
这种情况特别适合c++端将一些全局型或者可以确认对象生命周期肯定长于script evn周期的,比如一个联系人管理对象的生命周期要长于一个聊天对话框.html page的js 执行环境。
2. script env 控制型
script evn 将完全掌控c++ 对象的生命周期, 比如gc认为没有任何ref 2 this 对象时候; 这个模式特别适合那些本来就创建于或者为script evn创建的c++对象,比如:
QScriptValue myQObjectConstructor(QScriptContext *context, QScriptEngine *engine)
{
// let the engine manage the new object's lifetime.
return engine->newQObject(new MyQObject(), QScriptEngine::ScriptOwnership);
}
3. auto 模式
这个模式的含义是如果c++对象没有parent时候那么相当于script env ownership模式;否则就是qt ownership模式。
异常情况说明
假如有代码不管这个模式设置状况,在某个地点删除了c++对象,将引起?
1. script 端访问这个wrapper对象时候将触发exception
2. script 检查包装对象() 返回true,但是 () 返回null
控制对象访问
() 的第三个参数可以设定那些访问对脚本过滤。
script 端直接创建c++对象
目前qt 的meta object还不支持动态绑定构造函数。
().
You can also use () to wrap your own factory function, and add it to the script environment;
可以达到类似的目的。
类型专函问题,我记得去年我和刚刚搞v8 api layer的时候,写了很多模板,大致干的也是这个事情。
Default Conversion from Qt Script to C++
The following table describes the default conversion from a to a C++ type.
C++ Type | Default Conversion |
---|
bool | () |
int | () |
uint | () |
float | float(()) |
double | () |
short | short(()) |
ushort | () |
char | char(()) |
uchar | unsigned char(()) |
qlonglong | qlonglong(()) |
qulonglong | qulonglong(()) |
| An empty string if the is null or undefined; () otherwise. |
| () |
| ().() |
| () |
* | () |
* | () |
| () |
| If the is a string, the result is the first character of the string, or a null if the string is empty; otherwise, the result is a constructed from the unicode obtained by converting the to a ushort. |
| If the is an array, the result is a constructed from the result of () for each array element; otherwise, the result is an empty. |
| If the is an array, the result is a constructed from the result of () for each array element; otherwise, the result is an empty. |
| If the is an object, the result is a with a (key, value) pair of the form (propertyName, propertyValue.toVariant()) for each property, using to iterate over the object's properties. |
| If the is an array, the result is a constructed from the result of () for each array element; otherwise, the result is an empty. |
| If the is an array, the result is a constructed from the result of () for each array element; otherwise, the result is an empty. |
Default Conversion from C++ to Qt Script
The following table describes the default behavior when a is constructed from a C++ type:
C++ Type | Default Construction |
---|
void | () |
bool | (engine, value) |
int | (engine, value) |
uint | (engine, value) |
float | (engine, value) |
double | (engine, value) |
short | (engine, value) |
ushort | (engine, value) |
char | (engine, value) |
uchar | (engine, value) |
| (engine, value) |
qlonglong | (engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit integers can be represented using the qsreal type. |
qulonglong | (engine, qsreal(value)). Note that the conversion may lead to loss of precision, since not all 64-bit unsigned integers can be represented using the qsreal type. |
| (this, value.unicode()) |
| (value) |
| (value) |
| (value) |
* | (value) |
* | (value) |
| (value) |
| A new script array (created with ()), whose elements are created using the ( *, ) constructor for each element of the list. |
| A new script array (created with ()), whose elements are created using () for each element of the list. |
| A new script object (created with ()), whose properties are initialized according to the (key, value) pairs of the map. |
| A new script array (created with ()), whose elements are created using () for each element of the list. |
| A new script array (created with ()), whose elements are created using the ( *, int) constructor for each element of the list. |
Other types (including custom types) will be wrapped using (). For null pointers of any type, the result is ().
下面看几种特别的类型
1. 返回对象 指针
记住,qt只能识别qobject* qwidget* 这两个指针, 如果你给他传递其他指针,它是不能识别的。
因此建议在函数的signature 上仅仅使用 qobject* , qwidget* 作为接口指针。
或者使用自定义类型转换注册() .
2. script function object
脚本中的函数对象非常不同于c++的函数,它是个一级对象,可以有自己的属性,也可以作为一个对象传递和存储; 而且可以匿名。
因此了解函数对象的呼叫,定义在c++与js 非常有意义。
The following script defines a Qt Script object that has a toKelvin() function:
({ unitName: "Celsius",
toKelvin: function(x) { return x + 273; }
})
obtained and called from C++:
QScriptValue object = engine.evaluate("({ unitName: 'Celsius', toKelvin: function(x) { return x + 273; } })");
QScriptValue toKelvin = object.property("toKelvin");
QScriptValue result = toKelvin.call(object, QScriptValueList() << 100);
qDebug() << result.toNumber(); // 373
global function add():
function add(a, b) {
return a + b;
}
C++ code might call the add() function as follows:
QScriptValue add = engine.globalObject().property("add");
qDebug() << add.call(QScriptValue(), QScriptValueList() << 1 << 2).toNumber(); // 3
call 这个方法的第一个参数是this指针,表达wrappered js object; 如果非法或者null表示采用全局空间。
在js环境中理解this。。。
var getProperty = function(name) { return this[name]; };
name = "Global Object"; // creates a global variable
print(getProperty("name")); // "Global Object"
var myObject = { name: 'My Object' };
print(getProperty.call(myObject, "name")); // "My Object"
myObject.getProperty = getProperty;
print(myObject.getProperty("name")); // "My Object"
getProperty.name = "The getProperty() function";
getProperty.getProperty = getProperty;
getProperty.getProperty("name"); // "The getProperty() function"
在js中,这个this,一直到执行时候才evaluate , 不同于c++。 它的this并不和环境固定绑定,比如:
var o = { a: 1, b: 2, sum: function() { return a + b; } };
print(o.sum()); // reference error, or sum of global variables a and b!!
这基本上是c++ java程序员非常容易犯的错误。
将c++的函数包装为可让js访问。。。
QScriptValue getProperty(QScriptContext *ctx, QScriptEngine *eng)
{
QString name = ctx->argument(0).toString();
return ctx->thisObject().property(name);
}
Call () to wrap the function.
这样就可以创建一个wrapper 内部hold一个c++的函数,而可以被js使用
如何编码实现。。。?
Making a C++ object available to Scripts Written in QtScript
让c++的对象可以被script访问时意见很有趣和重要的事情,因为c++标准并不提供这种能力。
js是一种动态语言,要求能够运行时获取对象的信息,比如函数名,signature,属性等。
而这恰恰是c++标准没有定义的。
qt 扩展了c++的能力,这就是一点。
meta object system。
qtscript 就是使用了这个系统来支持动态访问c++对象。
1. 让class scritble
必须derived from qobject 这样script系统就可以动态获取class name,functionname,
signature,而有了这些信息,就可以支持动态访问。 当然q_OBJECT 需要定义。
2. 让成员函数scritble
默认的只要是qobject的subclas,它的public的slot就是脚本可见的。注意是public
class MyObject : public QObject
{
Q_OBJECT
public:
MyObject( ... );
void aNonScriptableFunction();
public slots: // these functions (slots) will be available in QtScript
void calculate( ... );
void setEnabled( bool enabled );
bool isEnabled() const;
private:
....
};
其实普通成员函数也可以很简单的scritble。。
比如上面例子中的anonscritablefunction 是脚本不可见的,但是只要 。。。
class MyObject : public QObject
{
Q_OBJECT
public:
Q_INVOKABLE void thisMethodIsInvokableInQtScript();
void thisMethodIsNotInvokableInQtScript();
...
};
就可以脚本可见的。。。
让属性scritable。。。
比如:
var obj = new MyObject;
obj.enabled = true;
print( "obj is enabled: " + obj.enabled );
class MyObject : public QObject
{
Q_OBJECT
// define the enabled property
Q_PROPERTY( bool enabled WRITE setEnabled READ isEnabled )
如果你不原意让你的属性可悲脚本访问,则可以
Q_PROPERTY(int nonScriptableProperty READ foo WRITE bar SCRIPTABLE false)
signal scriptable..
class MyObject : public QObject
{
Q_OBJECT
// define the enabled property
Q_PROPERTY( bool enabled WRITE setEnabled READ isEnabled )
public:
MyObject( ... );
void aNonScriptableFunction();
public slots: // these functions (slots) will be available in QtScript
void calculate( ... );
void setEnabled( bool enabled );
bool isEnabled() const;
signals: // the signals
void enabledChanged( bool newState );
private:
....
};
function enabledChangedHandler( b )
{
print( "state changed to: " + b );
}
function init()
{
var obj = new MyObject();
// connect a script function to the signal
obj["enabledChanged(bool)"].connect(enabledChangedHandler);
obj.enabled = true;
print( "obj is enabled: " + obj.enabled );
}
这样如何在c++端实现class,memfunc,slot,property,signal sctibable就ok了。
记得去年做demo时候,很多时候会接触到v8::context 这样一个东西, 比如注入的函数等都需要
通过这个对象,这个对象代表了一个js执行上下文,所以的变量等都出在这个上下文中。。
而qt port中存在一个
QScriptContext
- Get the arguments that were passed to the function.
- Get the this object.
- Find out whether the function was called with the new operator (the significance of this will be explained later).
- Throw a script error.
- Get the function object that's being invoked.
- Get the activation object (the object used to hold local variables).
参数需要注意两件事情
1. 任何脚本可call函数,它的参数数量可以是任意的。也就是说检查函数参数数量是函数的事情。
2. 参数可以被任何一个类型的输入apply, 也就是说类型检查是函数的事情。
qtscript 不对上述两点进行强制。
我们看个例子,就知道为何js 和 c++对参数都无所谓了。。
function add(a, b) {
return a + b;
}
function add() {
if (arguments.length != 2)
throw Error("add() takes exactly two arguments");
if (typeof arguments[0] != "number")
throw TypeError("add(): first argument is not a number");
if (typeof arguments[1] != "number")
throw TypeError("add(): second argument is not a number");
return arguments[0] + arguments[1];
}
function add() {
return arguments[0] + arguments[1];
}
function add() {
if (arguments.length != 2)
throw Error("add() takes exactly two arguments");
return arguments[0] + arguments[1];
}
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
if (ctx->argumentCount() != 2)
return ctx->throwError("add() takes exactly two arguments");
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
QScriptValue add(QScriptContext *ctx, QScriptEngine *eng)
{
if (ctx->argumentCount() != 2)
return ctx->throwError("add() takes exactly two arguments");
if (!ctx->argument(0).isNumber())
return ctx->throwError(QScriptContext::TypeError, "add(): first argument is not a number");
if (!ctx->argument(1).isNumber())
return ctx->throwError(QScriptContext::TypeError, "add(): second argument is not a number");
double a = ctx->argument(0).toNumber();
double b = ctx->argument(1).toNumber();
return a + b;
}
阅读(369) | 评论(0) | 转发(0) |