在一个将V8 JavaScript引擎与C ++代码连接起来的包装器中,我想调用一个C ++函数,按值传递一个对象。该对象由JavaScript中的数据自动构建。
要调用的C ++函数接受类型为T的对象,模板用于生成适配器函数A,按值返回T.问题是适配器函数A需要调用一个JavaScript函数,将另一个C ++函数B作为回调传递给它。类型T的对象是在该函数B中构造的。它不能通过JavaScript返回给A,JavaScript不知道如何处理类型为T的对象。
最简单的方法是在函数A中有一个类型为T的局部变量。给它的指针给B,它给局部变量赋一个新值,A稍后返回,大致如此(省略一些细节)关于如何将参数传递给callJavaScript和callback。实际上,T的构造函数以及B函数可以将任意数量的更复杂的类型作为参数):
C ++代码:
T A() {
T data;
callJavaScript("someJavaScriptFunction", &B, &data);
return data;
}
void B(T *data, int importantValue) {
*data = T(importantValue);
}
JavaScript代码:
function someJavaScriptFunction(callback, dataRef) {
callback(dataRef, getImportantValueSomehow());
}
但是如果类型T不支持赋值甚至有复制构造函数怎么办?有没有办法避免不必要的复制?我想过将函数A中的空格分配为局部变量:
typename std::aligned_storage<sizeof(T), alignof(T)>::type data;
然后,函数B可以使用placement new在该空间中构造对象,但是如何使用移动语义从A返回结果对象?如何正确调用可能的析构函数?
我最后的想法是使用更多的模板技巧为函数A中的类型T的构造函数分配空间,通过B中的指针设置它们,最后在A中构造对象,但如果某些参数中的数据变得令人讨厌callJavaScript返回时超出范围。有解决方案吗?
编辑:所有这一切的关键是将JavaScript对象的内容转换为C ++。从C ++读取对象的属性需要使用字符串按名称查找它们。 V8中的JIT编译函数可以更直接地访问对象的字段,并且someJavaScriptFunction中对象属性的读取可以编译成简单的指针读取。然后它可以使用各种参数调用C ++回调,这些参数从JavaScript值句柄转换为C ++类型的速度相当快。
EDIT2:简单的第一个想法是:
typename std::aligned_storage<sizeof(T), alignof(T)>::type data;
::new(&data) T(); // THIS LINE ACTUALLY PLACED IN ANOTHER FUNCTION
return(*reinterpret_cast<T *>(&data));
但是我应该为数据中构造的对象T调用析构函数,何时调用它,以及如何?这是一个库,并且向返回值的接收者添加代码实际上不是一个选项。
答案 0 :(得分:0)
我不确定我完全理解你的问题,但似乎你不需要在函数中传递输出参数。
简单地让函数A返回一个已经放入问题的值,并让B返回一个值。
由于“命名返回值优化”,因此不需要赋值运算符或复制构造函数。也就是说,如果您的代码满足“NRVO”的要求,那就没问题了:
T B(int importantValue) { return T{importantValue}; }
不需要赋值运算符也不需要复制构造函数。 然后,将callJavascript更改为不需要输出参数,但返回一个值,然后这将在没有复制构造函数或赋值运算符的情况下工作:
T A() { A rv{callJavascript(&B)}; return rv; }
通常,使其功能不需要输出参数,否则您需要类型具有复制构造函数和赋值运算符,或者违反类型系统。
顺便说一下,使callJavascript
使得它是一个以可调用的参数为模板的模板。
答案 1 :(得分:0)
我最终在A处使用了一个包装器来处理调用placement new和A的析构函数。它允许库的用户提供任何类A,只要它有一个移动构造函数。关于它的工作测试和讨论的完整代码可以在更新,更好的问题Placement new, return by value and safely dispose temporary copies及其接受的答案中找到。