如何使用相同的函数初始化类似的结构?

我有一些以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
点赞