在别处按值构造对象 return

Constructing an object to return by value elsewhere

在将 V8 JavaScript 引擎与 C++ 代码连接的包装器中,我想调用一个 C++ 函数,按值将对象传递给它。该对象是根据 JavaScript.

中的数据自动构建的

要调用的 C++ 函数采用 T 类型的对象,模板用于生成适配器函数 A,return按值调整 T。问题是适配器函数 A 需要调用一个 JavaScript 函数,将另一个 C++ 函数 B 作为回调传递给它。 T类型的对象是在那个函数B中构造出来的,它不能通过return回传给A,JavaScript,它不知道如何处理T类型的对象

最简单的方法是在函数A中有一个T类型的局部变量。将指向它的指针交给B,B为局部变量赋一个新值,A稍后returns,大致如此(关于如何将参数传递给 callJavaScript 和回调的一些细节省略了。实际上,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 中分配空 space 作为局部变量:

typename std::aligned_storage<sizeof(T), alignof(T)>::type data;

函数 B 然后可以使用新放置在 space 中构造对象,但是我如何使用移动语义从 A return 生成对象?如何正确调用可能的析构函数?

我最终的想法是使用更多的模板技巧来为函数 A 中类型 T 的构造函数分配 space 参数,通过 B 中的指针设置它们并最终在 A 中构造对象,但它会变得令人讨厌如果某些参数中的数据在调用 JavaScript returns 时超出范围。有解决办法吗?

编辑:所有这一切的目的是将 JavaScript 对象的内容放入 C++ 中。从 C++ 中读取对象的属性需要使用字符串按名称查找它们。 V8 中的 JIT 编译函数可以更直接地访问对象的字段,并且在某些 JavaScript 函数中读取对象的属性被编译为简单的指针读取。然后它可以使用各种参数调用 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));

但是我是否应该调用data中构造的对象T的析构函数,什么时候调用它,如何调用它?这是一个库,向 return 值的接收者添加代码并不是真正的选择。

我不确定我是否完全理解你的问题,但你似乎不需要在你的函数中传递输出参数。

只需将函数 A return 设置为您已经在问题中输入的值,将 B return 设置为一个值即可。

因为 "named return value optimization" 不需要赋值运算符和复制构造函数。也就是说,如果您的代码满足 "NRVO" 的要求,您就可以了:

T B(int importantValue) { return T{importantValue}; }

不需要赋值运算符,也不需要从 T 复制构造函数。 然后,将 callJavascript 更改为不需要输出参数,而是 return 一个值,然后这将在没有复制构造函数或赋值运算符的情况下工作:

T A() { A rv{callJavascript(&B)}; return rv; }

一般来说,让你的函数不需要输出参数,否则你需要你的类型有复制构造函数和赋值运算符,否则会违反类型系统。

顺便说一下,使 callJavascript 成为一个以可调用对象作为参数的模板。

我最终使用了 A 的包装器来处理调用 placement new 和 A 的析构函数。它允许库的用户提供任何 class A,只要它有一个移动构造函数。工作测试的完整代码和关于它的讨论可以在一个更新、更好的问题 及其接受的答案中找到。