Javascript 在Ductape中,必须保存一个Js回调函数,并将一个参数传递给C函数

Javascript 在Ductape中,必须保存一个Js回调函数,并将一个参数传递给C函数,javascript,c++,duktape,Javascript,C++,Duktape,C++调用JS函数JsFunc(),将C函数MyCFunc()作为参数传递。JsFunc()使用JS回调函数作为参数调用MyCFunc()pass main.cpp #include <duktape/src/duktape.h> #include <cassert> duk_ret_t MyCFunc(duk_context* ctx) { assert(duk_is_function(ctx, -1) ); (void) duk_require_f

C++调用JS函数JsFunc(),将C函数MyCFunc()作为参数传递。JsFunc()使用JS回调函数作为参数调用MyCFunc()pass

<如何保存MyCcultor()的JS回调函数参数,以便以后可以从C++中的其他地方调用它?< /P> main.cpp

#include <duktape/src/duktape.h>
#include <cassert>

duk_ret_t MyCFunc(duk_context* ctx) {
    assert(duk_is_function(ctx, -1) );
    (void) duk_require_function(ctx, -1);

    // 1.- How to save the callback function parameter
    //     so that it can be used later on, say in main()?
    return 0; // nothing returned
}

int main() {
    duk_context* ctx = duk_create_heap_default();
    assert(ctx != nullptr);

    if (duk_peval_file(ctx, "../../src/jscallback_forum/test.js") != 0) {
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
        exit(1);
    }
    duk_pop(ctx);  /* ignore result */

    duk_push_global_object(ctx);
    duk_bool_t isSuccess = duk_get_prop_string(ctx, -1 , "JsFunc");
    assert(isSuccess != false);

    // pass  MyCFunc as parameter to JsFunc
    duk_push_c_function(ctx, &MyCFunc, 1); // MyCFunc expects Js callback

    if (duk_pcall(ctx, 1) != 0) { // JsFunc call failed
        printf("Error: %s\n", duk_safe_to_string(ctx, -1));
    }

    duk_pop(ctx);  /* pop duk_pcall result/error */
    duk_pop(ctx);  /* pop duk_push_global_object */

// 2. How do I retrieve the JS callback function 
    //    saved in MyCFunc() and run it?

    duk_destroy_heap(ctx);

    return 0;
}

原则上,与类似的Ecmascript函数没有区别: 接受回调的C函数(
MyCFunc
在您的示例中)需要在返回之前将参数回调存储到一个更持久的位置,以便以后可以查找它

该存储位置有多个选项;等效的Ecmascript函数可能会将引用存储到全局对象中,或存储在全局对象中的某些数据结构中(例如
回调
数组)。从C中使用Duktape时,您还可以使用Duktape提供的一个“隐藏”对象(请参阅),这些对象对Ecmascript代码不可见

作为一个具体示例,假设一次只存储一个回调,下面介绍如何将回调存储到全局对象中:

duk_ret_t MyCFunc(duk_context *ctx) {
    /* Value stack index 0 has callback function. */

    /* Equivalent to Ecmascript code: globalObject._my_callback = arg; */
    duk_dup(ctx, 0);
    duk_put_global_string(ctx, "_my_callback");
    return 0;
}
然后,稍后当您要调用它时:

duk_int_t rc;

/* ... */

duk_get_global_string(ctx, "_my_callback");
rc = duk_pcall(ctx, 0);  /* no arguments in this example */
if (rc != 0) {
    printf("Callback failed: %s\n", duk_safe_to_string(ctx, -1));
} else {
    printf("Callback success\n");
}
duk_pop(ctx);  /* pop result */

谢谢你的回复。在我发布这个问题后不久,我找到了一个与您建议相匹配的解决方案,即使用duk_put_global_string()和duk_get_global_string()。但是,正如您也指出的,此解决方案仅适用于一次只执行一个回调的情况。在我的实际应用程序中,JsFunc()由许多客户端在不同的线程上调用。我发现最好的解决方案是使用线程隐藏(而不是全局隐藏),你不同意吗?您将如何使用它?在Ecmascript代码中所使用的方法没有真正的区别(除了一些额外的功能,比如隐藏)。典型的解决方案是为每个回调分配一个标识符,并在数组或对象中维护它们。该句柄(例如数字ID)被跟踪,以便在需要回调时,该ID可用于查找回调。关于要使用的隐藏,请澄清:“线程隐藏”范围是Duktape线程(也称为“上下文”),而不是本机线程。如果回调确实通过不同的Duktape线程发生,那么线程隐藏将是一个可行的解决方案。但是一个更通用的解决方案是在“挂起的回调”状态下跟踪某种回调ID,以便在需要回调时可用。感谢您花时间让事情变得更清楚。是的,线程隐藏是上下文,而不是本机线程。然而,不知何故,我确实更喜欢“线程隐藏”解决方案,而不是对存储在一个位置的回调使用全局唯一ID(请注意,条目越多,ID的sarch花费的时间就越长)。通过使用与上下文相关联的“线程隐藏”以及存储回调的位置,ID不必是唯一的,隐藏很小,因此搜索速度很快。我已经试过了,而且似乎有效。我希望有DukPad API的C++类包装!
duk_int_t rc;

/* ... */

duk_get_global_string(ctx, "_my_callback");
rc = duk_pcall(ctx, 0);  /* no arguments in this example */
if (rc != 0) {
    printf("Callback failed: %s\n", duk_safe_to_string(ctx, -1));
} else {
    printf("Callback success\n");
}
duk_pop(ctx);  /* pop result */