构造一个对象以在其他地方按值返回

Constructing an object to return by value elsewhere

本文关键字:方按值 返回 其他 一个对象      更新时间:2023-10-16

在一个用C++代码连接V8 JavaScript引擎的包装器中,我想调用一个按值传递对象的C++函数。该对象是根据JavaScript内部的数据自动构建的。

要调用的C++函数接受类型为T的对象,并使用模板生成适配器函数a,按值返回T。问题是适配器函数A需要调用一个JavaScript函数,将另一个C++函数B作为回调传递给它。T类型的对象是在函数B中构造的。它不能通过JavaScript返回给A,因为JavaScript不知道如何处理T类型的物体。

最简单的方法是在函数a中有一个T类型的局部变量。指向它的指针被赋予B,B为局部变量分配一个新值,a稍后返回,大致如此(关于如何将参数传递给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内部的空白空间分配为一个局部变量:

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

然后,函数B可以使用placement new在该空间中构造对象,但我如何使用move语义从A返回结果对象?如何正确调用可能的析构函数?

我的最终想法是使用更多的模板技巧来为函数A中的类型T的构造函数分配参数空间,通过B中的指针设置它们,并最终在A中构造对象,但当callJavaScript返回时,如果某些参数中的数据超出范围,就会变得很糟糕。有解决方案吗?

EDIT:所有这些的目的是将JavaScript对象的内容获取到C++中。从C++中读取对象的属性需要使用字符串按名称查找。V8中的JIT编译函数可以更直接地访问对象的字段,并且在someJavaScriptFunction中读取对象的属性可以编译成简单的指针读取。然后,它可以用各种参数调用C++回调,这些参数从JavaScript值句柄转换为C++类型的速度相当快。

编辑2:简单的第一个想法是:

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调用析构函数吗?何时调用,以及如何调用?这是一个库,向返回值的接收者添加代码实际上不是一个选项。

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

简单地让函数A返回一个值,就像你在问题中已经输入的那样,让B返回一个数值。

由于"命名返回值优化",不需要赋值运算符也不需要复制构造函数。也就是说,如果你的代码满足"NRVO"的要求,你就可以了:

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

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

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

通常,要确保函数不需要输出参数,否则就要求类型具有复制构造函数和赋值运算符,或者违反类型系统。

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

我最终在a周围使用了一个包装器,该包装器处理调用placement new和a的析构函数。它允许库的用户提供任何类A,只要它有一个移动构造函数。工作测试的完整代码和关于它的讨论可以在一个更新的、公式化更好的问题中找到。放置新的、按值返回并安全地处理临时副本及其接受的答案。