在堆栈上分配任意常量大小的结构
Allocating structs of arbitrary constant size on the stack
我写了一个小型的工作插件服务器。插件是使用 .so
共享 objects 实现的,它们在运行时通过调用 dlopen
(header <dlfcn.h>
) 在“服务器”中手动加载。
所有共享的 object 插件具有相同的界面:
extern "C" void* do_something() {
return SharedAllocator<T>{}.allocate(...); // new T
}
extern "C" size_t id = ...; // unique
- 基本上,
do_something
return 是一个指向堆内存的指针,调用者应该释放它。
id
只是每个 .so
. 的唯一标识符
T
是特定于每个 .so
的结构。其中有些共享相同的 return 类型,有些则不同。这里的重点是,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
never 改变任意调用站点。
供参考,这里是 all 呼叫者使用的定义:
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
总是具有相同的 return 类型。
如果不是代理,我可以只模板 do_soemthing_proxy
并传递 T
或 std::array<int8_t, N>
和 sizeof(T) == N
,并将不必要的内存分配给确保 T
在调用 do_something_proxy
可以移动到堆栈时不被切片。但是,代理无法在编译期间了解所有可能的 return 类型,并且无法导出 do_something_proxy
.
的无数版本
所以我的问题是,do_soemthing_proxy
有没有办法在其堆栈上分配T
的有效大小(即使用alloca
或其他形式的堆栈分配)?
据我所知,alloca
在这里似乎不起作用,因为 do_soemthing_proxy
只能从所请求插件的 do_something
函数接收单个值。 do_soemthing_proxy
将同时接收要分配的大小和要复制到已分配内存的字节数。如果只有 alloca
可以在两者之间被“压扁”...
我知道我可以使用 std::array<int8_t, N>
在堆栈上分配固定数量的内存,其中 N
的值为 256 甚至 1024。但是,这个解决方案有点脏。它不必要地将数据从一个堆栈框架复制到另一个堆栈框架,并限制了插件可以 return 的数据量。最重要的是,(虽然我还没有对这个解决方案进行基准测试)除非编译器可以跨越动态边界删除副本,我假设复制 1024 字节比复制即 sizeof(std::string)
字节更多的工作。
在理想情况下,我相信 do_soemthing_proxy
应该 return 一个用 RAII 处理这个问题的结构。 const std::any
即 stack-allocated,如果你愿意的话。这可能吗?
如果这在 c++ 中根本不可能,是否可以在汇编中以可移植的方式实现此行为,即通过手动劫持堆栈或基指针?
谢谢。
其实,我刚刚找到了解决办法。归结为反转分配 T
的内存位置的方向。
Is there any way for do_soemthing_proxy
to allocate the effective size of T
on its stack?
也许吧。但代码实际需要的是在调用者位置分配 T
的有效大小,而不是在代理内部。由于调用者知道 sizeof(T)
,您所要做的就是在调用者 调用 之前在调用者的堆栈上为 T
分配 space 19=],然后在调用的时候把分配的buffer的地址传给do_something_proxy
:
来电者:
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
}
我写了一个小型的工作插件服务器。插件是使用 .so
共享 objects 实现的,它们在运行时通过调用 dlopen
(header <dlfcn.h>
) 在“服务器”中手动加载。
所有共享的 object 插件具有相同的界面:
extern "C" void* do_something() {
return SharedAllocator<T>{}.allocate(...); // new T
}
extern "C" size_t id = ...; // unique
- 基本上,
do_something
return 是一个指向堆内存的指针,调用者应该释放它。 id
只是每个.so
. 的唯一标识符
T
是特定于每个.so
的结构。其中有些共享相同的 return 类型,有些则不同。这里的重点是,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
never 改变任意调用站点。
供参考,这里是 all 呼叫者使用的定义:
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
总是具有相同的 return 类型。
如果不是代理,我可以只模板 do_soemthing_proxy
并传递 T
或 std::array<int8_t, N>
和 sizeof(T) == N
,并将不必要的内存分配给确保 T
在调用 do_something_proxy
可以移动到堆栈时不被切片。但是,代理无法在编译期间了解所有可能的 return 类型,并且无法导出 do_something_proxy
.
所以我的问题是,do_soemthing_proxy
有没有办法在其堆栈上分配T
的有效大小(即使用alloca
或其他形式的堆栈分配)?
据我所知,alloca
在这里似乎不起作用,因为 do_soemthing_proxy
只能从所请求插件的 do_something
函数接收单个值。 do_soemthing_proxy
将同时接收要分配的大小和要复制到已分配内存的字节数。如果只有 alloca
可以在两者之间被“压扁”...
我知道我可以使用 std::array<int8_t, N>
在堆栈上分配固定数量的内存,其中 N
的值为 256 甚至 1024。但是,这个解决方案有点脏。它不必要地将数据从一个堆栈框架复制到另一个堆栈框架,并限制了插件可以 return 的数据量。最重要的是,(虽然我还没有对这个解决方案进行基准测试)除非编译器可以跨越动态边界删除副本,我假设复制 1024 字节比复制即 sizeof(std::string)
字节更多的工作。
在理想情况下,我相信 do_soemthing_proxy
应该 return 一个用 RAII 处理这个问题的结构。 const std::any
即 stack-allocated,如果你愿意的话。这可能吗?
如果这在 c++ 中根本不可能,是否可以在汇编中以可移植的方式实现此行为,即通过手动劫持堆栈或基指针?
谢谢。
其实,我刚刚找到了解决办法。归结为反转分配 T
的内存位置的方向。
Is there any way for
do_soemthing_proxy
to allocate the effective size ofT
on its stack?
也许吧。但代码实际需要的是在调用者位置分配 T
的有效大小,而不是在代理内部。由于调用者知道 sizeof(T)
,您所要做的就是在调用者 调用 之前在调用者的堆栈上为 T
分配 space 19=],然后在调用的时候把分配的buffer的地址传给do_something_proxy
:
来电者:
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
}