4.3、4.4节介绍了如何将C++对象导出到JavaScript环境,本节将介绍其逆操作,既:将JavaScript对象注入到C环境。
无论从内存模型的角度,还是从运行模型的角度,C原生代码都无法直接访问JavaScript中的对象。为此,我们需要提供一种途径,让C环境可以识别不同的JavaScript对象,最容易想到的,就是使用对象/ID表,该方法的核心是:
tips 事实上这种方法与4.3节中介绍的C++对象导出方法使用的是相同的思路,区别仅在于4.3节中使用的是C++对象的地址来指代C++对象本身。
下面的JavaScript代码是一个简单的使用对象/ID表的例子:
var obj_table = {}; var obj_counter = 0; function MyObj_Create() { this.name = "MyObj"; this.obj_id = obj_counter++; obj_table[this.obj_id] = this; } function MyObj_Func(obj_id) { if (!obj_table[obj_id]) return; //do sth. with obj_table[obj_id] }
obj_table是对象/ID表;构造器方法MyObj_Create()创建一个新的对象时,将为该对象分配ID,并在obj_table中保存对应关系;MyObj_Func()方法通过传入的obj_id参数反查对应的对象,并使用它。
obj_table
MyObj_Create()
MyObj_Func()
obj_id
JavaScript对象注入C环境后,仍然需要对其进行生命周期控制。4.4节使用的AddRef()/Release()方法同样可行。
AddRef()
Release()
例如我们定义一组JavaScript方法如下:
//imp_obj.html var button_table = {}; var button_counter = 0; function js_ButtonCreate() { var btn = document.createElement("button"); btn.ref_count = 1; btn.button_id = button_counter++; button_table[btn.button_id] = btn; document.getElementById("container").appendChild(btn); return btn.button_id; } function js_ButtonAddRef(button_id) { if (!button_table[button_id]) return; button_table[button_id].ref_count++; } function js_ButtonRelease(button_id) { if (!button_table[button_id]) return -1; var btn = button_table[button_id]; btn.ref_count--; var rc = btn.ref_count; if (rc == 0) { document.getElementById("container").removeChild(btn); delete button_table[button_id]; } return rc; } function js_ButtonSetInnerHtml(button_id, name) { if (!button_table[button_id]) return; button_table[button_id].innerHTML = name; }
js_ButtonCreate()用于在DOM中创建按钮,js_ButtonAddRef()/js_ButtonRelease()分别用于增减按钮的引用计数,js_ButtonSetInnerHtml()用于设置指定按钮的内部html内容。参照4.5.1节,这4个函数围绕按钮/ID表button_table工作。
js_ButtonCreate()
js_ButtonAddRef()
js_ButtonRelease()
js_ButtonSetInnerHtml()
button_table
然后我们使用2.2节的方法将这些方法注入到C环境中,导入库代码如下:
//pkg.js mergeInto(LibraryManager.library, { ButtonCreate: function () { return js_ButtonCreate(); }, ButtonAddRef: function (button_id) { js_ButtonAddRef(button_id); }, ButtonRelease: function (button_id) { return js_ButtonRelease(button_id); }, ButtonSetInnerHtml: function(button_id, str) { js_ButtonSetInnerHtml(button_id, Pointer_stringify(str)); } })
C接口及导出函数如下:
//imp_obj.cpp struct JS_BUTTON; EM_PORT_API(struct JS_BUTTON*) ButtonCreate(); EM_PORT_API(void) ButtonAddRef(struct JS_BUTTON* btn); EM_PORT_API(int) ButtonRelease(struct JS_BUTTON* btn); EM_PORT_API(void) ButtonSetInnerHtml(struct JS_BUTTON* btn, const char* str); //----------------------------------- std::vector<struct JS_BUTTON*> g_buttons; EM_PORT_API(void) PushButton() { JS_BUTTON* btn = ButtonCreate(); char name[256]; sprintf(name, "TestButton:%d", (int)btn); ButtonSetInnerHtml(btn, name); g_buttons.push_back(btn); } EM_PORT_API(void) PopButton() { if (g_buttons.size() <= 0) return; JS_BUTTON* btn = g_buttons.back(); ButtonRelease(btn); g_buttons.pop_back(); }
使用下列命令编译:
emcc imp_obj.cpp --js-library pkg.js -o imp_obj.js
然后我们在网页中创建两个按钮,并分别在其onclick事件中调用PushButton()/PopButton():
onclick
PushButton()
PopButton()
<button onclick = Push()>Push</button> <button onclick = Pop()>Pop</button> <script> ...... function Push(){ Module._PushButton(); } function Pop(){ Module._PopButton(); }
浏览页面,点击Push/Pop,可以看到DOM中创建/删除了多个按钮:
Push
Pop
调用顺序如下图所示:
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8