绑定全局函数
本节介绍了如何绑定全局函数
例子
假设我们有一个简单的Add
函数:
int Add(int a, int b) {
return a + b;
}
我们和绑定变量一样绑定他到全局对象。只是使用JS_NewCFunction
创建JSValue
:
JSValue global_this = JS_GetGlobalObject(ctx);
constexpr int FnParamCount = 2;
JSValue fn = JS_NewCFunction(ctx, AddFnBinding, "Add", FnParamCount);
JS_SetPropertyStr(ctx, global_this, "Add", fn);
JS_FreeValue(ctx, global_this);
注意JS_NewCFunction
的第二个参数AddFnBinding
。他的类型是JSCFunction
,一个函数指针:
typedef JSValue JSCFunction(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv);
QuickJS在看到JS调用C++函数时其实会调用我们给的这个函数指针。我们需要实现此函数指针并且在内部调用我们自己的C++函数:
JSValue AddFnBinding(JSContext* ctx, JSValue self, int argc,
JSValueConst* argv) {
if (argc != 2) {
return JS_ThrowPlainError(ctx, "Add function must has two parameters");
}
JSValueConst param1 = argv[0];
JSValueConst param2 = argv[1];
if (!JS_IsNumber(param1) || !JS_IsNumber(param2)) {
return JS_ThrowTypeError(ctx, "Add accept two integral");
}
int32_t value1, value2;
JS_ToInt32(ctx, &value1, param1);
JS_ToInt32(ctx, &value2, param2);
return JS_NewInt32(ctx, Add(value1, value2));
}
参数解释:
-
ctx
:JS上下文 -
self
:如果函数是类对象函数,那这里会传入类对象。否则是JS_UNDEFINED
-
argc
:传给函数的参数格式 -
argv
:传给函数的参数。注意这里写的虽是JSValueConst
但并不代表他们是常量。因为底层定义如下:#define JSValueConst JSValue
这里首先检查参数个数和参数类型是否是我们想要的,然后将两个参数使用JS_ToXXX
转换成C++类型,然后调用我们的Add
函数并将结果穿给JSValue
。
更多函数类型
QuickJS内置了很多函数类型。JSCFunction
只是最通用和最常见的类型。还有如下类型(定义在JSCFunctionEnum
):
JS_CFUNC_generic
:最通用的函数类型(就是我们使用的JSCFunction
)JS_CFUNC_constructor
:类的构造函数- 专门给数学函数定义的类型:
JS_CFUNC_f_f
:形如double(*)(double)
的函数指针
JS_CFUNC_f_f_f
:形如double(*)(double, double)
的函数指针JS_CFUNC_getter
和JS_CFUNC_setter
:getter和setterJS_CFUNC_iterator_next
:用于迭代器的类型JS_CFUNC_constructor_or_func
:<不清楚是什么,以后研究>- 以及某些函数的magic版本(在类型后面加
_magic
例如JS_CFUNC_generic_magic
)
这些函数的签名可以在JSCFunctionType
看到:
typedef union JSCFunctionType {
JSCFunction *generic;
JSValue (*generic_magic)(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor;
JSValue (*constructor_magic)(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv, int magic);
JSCFunction *constructor_or_func;
double (*f_f)(double);
double (*f_f_f)(double, double);
JSValue (*getter)(JSContext *ctx, JSValueConst this_val);
JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val);
JSValue (*getter_magic)(JSContext *ctx, JSValueConst this_val, int magic);
JSValue (*setter_magic)(JSContext *ctx, JSValueConst this_val, JSValueConst val, int magic);
JSValue (*iterator_next)(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int *pdone, int magic);
} JSCFunctionType;
数学函数类型
JS_CFUNC_f_f
和JS_CFUNC_f_f_f
。
这两个函数都是接收double
作为参数(f_f
接收一个,f_f_f
接收两个),返回一个double
值。一般用于绑定sin
,cos
这种数学函数。优点是函数签名不含JSValue
直接是double
,不需要做额外的转换。
getter和setter
用于变量的Getter和Setter。可以使用这种方式实现只读变量(只实现getter不实现setter)。函数签名分别是:
Getter
:JSValue (*getter)(JSContext *ctx, JSValueConst this_val)
。传入要获得值的对象Setter
:JSValue (*setter)(JSContext *ctx, JSValueConst this_val, JSValueConst val)
。传入值的来源val
和放入值的变量this_val
迭代器类型
<暂时没研究,之后补上>
带有magic的类型
几乎所有类型的函数都有magic版本。magic版本的作用是通过额外的参数int magic
来将多个函数聚合在一起:
// binding function
JSValue BindMagicFn(JSContext* ctx, JSValue, int argc, JSValueConst* argv, int magic) {
if (magic == 0) {
MagicFn1();
} else if (magic == 1) {
MagicFn2();
}
return JS_UNDEFINED;
}
// bind
JSValue fn1 = JS_NewCFunctionMagic(ctx, BindMagicFn, "MagicFn1", FnParamCount, JS_CFUNC_generic_magic, 0);
JSValue fn2 = JS_NewCFunctionMagic(ctx, BindMagicFn, "MagicFn2", FnParamCount, JS_CFUNC_generic_magic, 1);
使用JS_NewCFunctionMagic
来绑定magic函数。通过最后的参数来区分内部到底是在使用哪个函数。
如何绑定这些花里胡哨的函数类型
虽然函数类型和签名很多,但是JS_SetPropertyStr
的第二个参数只接收JSCFunction
类型。要如何绑定呢?有两种方法:
-
使用特定的绑定函数,比如magic相关的就是
JS_NewCFunctionMagic
,getter/setter就是JS_DefinePropertyGetSet()
-
使用
JSCFunctionType
进行转换:JSCFunctionType fn_type; // JS_CFUNC_f_f_f pass two double elem and return one double elem fn_type.f_f_f = +[](double param1, double param2) -> double { return param1 + param2; }; JSValue fn = JS_NewCFunction2(ctx, fn_type.generic, "Sum", 1, JS_CFUNC_f_f_f, 0);