在堆栈上分配任意常量大小的结构

Allocating structs of arbitrary constant size on the stack

本文关键字:结构 常量 任意 堆栈 分配      更新时间:2024-09-27

我已经编写了一个小型的插件服务器。插件是使用.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));
}

换句话说,对于某个任意插件dlopen2,do_something_proxy总是具有相同的返回类型。

如果不是因为代理,我可以只模板do_soemthing_proxy并传递T或带有sizeof(T) == Nstd::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分配空间,然后在调用时将分配的缓冲区的地址传递给id0:

对于呼叫者:

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
}