我写了一个小型的工作插件服务器。插件是使用.so
共享对象实现的,这些共享对象在运行时通过调用dlopen
(header <dlfcn.h>
) 在“服务器”中手动加载。
所有共享对象插件都具有相同的接口:
extern "C" void* do_something() {
return SharedAllocator<T>{}.allocate(...); // new T
}
extern "C" size_t id = ...; // unique
- 基本上,
do_something
返回一个指向堆内存的指针,调用者应该释放它。 id
只是一个唯一的标识符 per.so
。T
是每个特定的结构.so
。其中一些共享相同的返回类型,而另一些则没有。这里的重点是,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
可能因呼叫站点而异;但是对于任意呼叫站点T
永远不会改变。
作为参考,这是所有调用者使用的定义:
template <typename T, size_t id>
T* typed_do_soemthing_proxy() {
// simple cast of the proxy
return reinterpret_cast<T*>(do_soemthing_proxy(id));
}
换句话说,do_something_proxy
对于某些任意插件,id
总是具有相同的返回类型。
如果不是为了代理,我可以只使用模板do_soemthing_proxy
并传递T
或使用std::array<int8_t, N>
with ,并且当调用可以移动到堆栈时sizeof(T) == N
,分配的不必要的内存不会被切片。但是,代理无法在编译期间知道所有可能的返回类型并导出无数版本的.T
do_something_proxy
do_something_proxy
所以我的问题是,有没有办法在其堆栈上do_soemthing_proxy
分配有效大小T
(即使用alloca
或其他形式的堆栈分配)?
据我所知,alloca
这里似乎不起作用,因为do_soemthing_proxy
只能从do_something
请求的插件的函数中接收一个值。do_soemthing_proxy
将同时接收要分配的大小和要复制到分配的内存的字节。要是alloca
能在两者之间“压扁”就好了……
std::array<int8_t, N>
我知道我可以使用256 甚至 1024 的值在堆栈上分配固定数量的内存N
。但是,这个解决方案有点脏。它不必要地将数据从一个堆栈帧复制到另一个堆栈帧,并限制了插件可以返回的数据量。最重要的是,(虽然我还没有对这个解决方案进行基准测试)除非编译器可以跨越动态边界删除副本,否则我假设复制 1024 字节比复制 iesizeof(std::string)
字节更工作。
在一个理想的世界里,我相信do_soemthing_proxy
应该返回一个用 RAII 处理这个的结构。一个const std::any
堆栈分配的,如果你愿意的话。这甚至可能吗?
如果这在 c++ 中根本不可能,是否可以在汇编中以可移植的方式实现这种行为,即通过手动劫持堆栈或基指针?
谢谢。