我已经编写了一个小型的插件服务器。插件是使用.so
共享对象来实现的;服务器";通过对CCD_ 2(报头<dlfcn.h>
(的调用。
所有共享对象插件都有相同的接口:
extern "C" void* do_something() {
return SharedAllocator<T>{}.allocate(...); // new T
}
extern "C" size_t id = ...; // unique
- 基本上,
do_something
返回一个指向堆内存的指针,调用方应释放该指针 - CCD_ 5只是每个CCD_ 6唯一的标识符
- CCD_ 7是每个CCD_。其中一些具有相同的返回类型,而另一些则没有。这里的重点是,
sizeof(T)
是.so
特有的
服务器负责动态加载和读取.so
二进制文件的符号。所有.so
插件都可以通过服务器二进制文件中定义的方法do_something_proxy
相互调用,该方法充当调用方和被调用方之间的粘合剂:
extern "C" void* do_something_proxy(size_t id) {
// find the requested handle
auto handle = some_so_map.find(id)->second;
// call the handle's `do_something`
void* something_done = handle.do_something();
// forward the result
return something_done;
}
为了稍微简化一下,假设some_so_map
是一个普通的std::unordered_map<size_t, so_handle_t>
,在执行代理时使用对dlopen
的一组调用填充。
我的问题是do_something_proxy
的每个调用者在编译时都知道T
。正如我前面所说的,T
可能因呼叫站点而异;但是CCD_ 20对于任意呼叫站点从不更改。
作为参考,以下是所有调用方使用的定义:
template <typename T, size_t id>
T* typed_do_soemthing_proxy() {
// simple cast of the proxy
return reinterpret_cast<T*>(do_soemthing_proxy(id));
}
换句话说,对于某个任意插件dlopen
2,do_something_proxy
总是具有相同的返回类型。
如果不是因为代理,我可以只模板do_soemthing_proxy
并传递T
或带有sizeof(T) == N
的std::array<int8_t, N>
,并且在调用do_something_proxy
时为确保T
不被切片而分配的不必要内存可以移动到堆栈中。然而,代理在编译时不能知道所有可能的返回类型,并导出无数版本的do_something_proxy
。
所以我的问题是,do_soemthing_proxy
有没有办法在其堆栈上分配T
的有效大小(即使用alloca
或其他形式的堆栈分配(?
据我所知,alloca
在这里似乎不起作用,因为do_soemthing_proxy
只能从所请求插件的do_something
函数中接收一个值。do_soemthing_proxy
将同时接收要分配的大小和要复制到分配的存储器的字节。如果只有CCD_;压扁的";介于。。。
我知道我可以使用std::array<int8_t, N>
在堆栈上分配固定数量的内存,CCD_38的值为256甚至1024。但是,这个解决方案有点脏。它不必要地将数据从一个堆栈帧复制到另一个堆栈框架,并限制插件可以返回的数据量。最重要的是,(虽然我还没有对这个解决方案进行基准测试(除非编译器可以消除跨动态边界的复制,否则我认为复制1024字节比复制sizeof(std::string)
字节要多得多。
在理想的情况下,我认为do_soemthing_proxy
应该返回一个使用RAII处理此问题的结构。一个const std::any
,如果您愿意的话,它是堆栈分配的。这可能吗?
如果这在c++中根本不可能,那么在汇编中是否可以以可移植的方式实现这种行为,即手动劫持堆栈或基指针?
谢谢。
实际上,我刚刚找到了一个解决方案。它可以归结为反转用于分配T
的存储器位置的传递方向。
do_soemthing_proxy
有没有办法在其堆栈上分配T
的有效大小?
也许吧。但代码实际需要的是在调用者的位置分配T
的有效大小,而不是在代理内部。由于调用者知道sizeof(T)
,您所要做的就是在调用do_something
之前,在调用者的堆栈上为T
分配空间,然后在调用时将分配的缓冲区的地址传递给id
0:
对于呼叫者:
template <typename T, size_t id>
T typed_do_something_proxy() {
std::aligned_storage_t<sizeof(T), alignof(T)> return_buffer;
do_something_proxy(id, &return_buffer);
return *std::launder(reinterpret_cast<T*>(&return_buffer));
}
代理:
extern "C" void do_something_proxy(size_t id, void* return_buffer) {
auto handle = some_so_map.find(id)->second;
handle.do_something(return_buffer);
}
对于被叫
extern "C" void do_something(void* return_buffer) {
new(return_buffer) T(...); // placement new
}