我有一些以void *缓冲区开头的结构,下一个成员可以是不同的类型:
struct A {
void *buffer;
type_x *x;
type_y *y;
};
struct B {
void *buffer;
type_w *w;
type_y *y;
type_z *z;
};
struct A的缓冲区将存储类型为type_x的n个元素,后跟n个type_y元素.其他成员type_x * x和type_y * y将分别指向这些数组.类似于结构B.
我目前正在做的是这样的:
void allocate(struct B **b, unsigned int n) {
(*b)->buffer = calloc(n * (sizeof(type_w) + sizeof(type_y) + sizeof(type_z));
(*b)->w = (type_w *) (*b)->buffer;
(*b)->y = (type_y *) ((*b)->w + n);
(*b)->z = (type_z *) ((*b)->y + n);
}
有没有办法创建一个功能来实现这一目标?该函数应作为参数接收指向其中一个结构(void * s)和一个int的指针,如下所示:
void allocate(void *s, unsigned int n) {
// MAGIC
}
我考虑过的其他一些选择:
>创建void allocate(void * buffer,int n,…)并给它指向struct的指针.这个问题是我必须给它void *指针,所以我必须给它每个类型的大小.
>创建void * create_struct(StructType类型)(其中StructType是枚举)但我必须为每个结构编写案例,我希望能够定义新的结构而不必编写附加代码.
我正在尝试这样做,因为我将有许多结构,并且因为allocate函数对于每个结构基本上都是相同的,我认为可能有一个“更清洁”的方法来做它.
另外,我知道我可以删除缓冲区并直接为所有成员分配内存,但我想这样做,因此数据是连续存储的.
最佳答案 没有通用的方法可以实现快速和松散的类型安全性.这意味着,从技术上讲,真正通用的解决方案将导致未定义的行为.如果我被迫实现这样的东西,我将不得不假设我可以将传入的结构指针视为一个指针数组.并且需要传入每种类型的大小.忽略对齐问题,一些未经测试的代码:
void allocate(void *sp, size_t n, ... /* terminate with 0 */) {
void **sv = sp;
size_t arg, total = 0;
size_t args = 0;
va_list ap;
va_start(ap, n);
while ((arg = va_arg(ap, size_t)) != 0) {
total += arg;
++args;
}
va_end(ap);
*sv = calloc(...);
sv[1] = sv[0];
va_start(ap, n);
while (--args > 0) {
++sv;
sv[1] = (char *)sv[0] + va_arg(ap, size_t);
}
va_end(ap);
}
allocate(a, n, sizeof(type_x), sizeof(type_y), (size_t)0);
allocate(b, n, sizeof(type_w), sizeof(type_y), sizeof(type_z), (size_t)0);
显然是hacky和丑陋.
更好的解决方案应该是为每种类型创建一个单独的分配器函数.但是,您可以创建一个宏来帮助自动生成分配器.更多未经测试的代码如下:
#define CREATE_ALLOCATOR(Type, X_Fields) \
void allocate_##Type (struct Type *sp, size_t n) { \
_Pragma("pop_macro(\"X\")") \
size_t total = 0 \
X_Fields \
; \
void *p; \
sp->buffer = calloc(sizeof(*sp) + total); \
p = sp->buffer; \
_Pragma("pop_macro(\"X\")") \
X_Fields \
; \
}
#include "create_allocator_helper.h"
CREATE_ALLOCATOR(A, X(x) X(y))
#include "create_allocator_helper.h"
CREATE_ALLOCATOR(B, X(w) X(y) X(z))
辅助头文件定义并推送CREATE_ALLOCATOR宏使用的一些X宏定义:
#ifdef X
#undef X
#endif
#define X(A) ; sp->A = p; p = sp->A + n
#pragma push_macro("X")
#undef X
#define X(A) + sizeof(sp->A)
#pragma push_macro("X")
#undef X