带有可变长度尾部的普通 "C" 静态初始化程序宏。

Plain "C" static initializer macro with variable length tail.

我有一个结构定义为:

typedef struct coro_context {
    int id;
    jmp_buf env;
    list_head list;
    jmp_buf waiter;
    long timeout;
    void *private;
    char stack[0];
} coro_context;

我需要用三个值初始化

我目前的尝试看起来像(“unsigned long”位是为了获得正确的对齐方式):

#define CORO_CONTEXT(name,size) \
        unsigned long __##name##_ctx[(sizeof(coro_context)+size)/sizeof(unsigned long)]={0};\
        coro_context *name##_ctx = (coro_context *)__##name##_ctx

这可行,但有两个问题(好的,问题是一个半;)):

注意: 我坚持使用 "static initialization" 因为我想在普通 "C" 中使用它(c11 可以,如果需要的话)因为我需要在函数上下文之外使用这些初始化。

首先请注意,long 缓冲区的大小可能与您的预期不符,因为它四舍五入到 floor。例如,如果 core_context 大小为 10 个字节,并且一个请求的额外大小为 3 个字节,并假设 long 大小为 8 个字节。你得到 (10+3)/8 = 1,所以你分配 1 个 long 来处理 13 个字节(而不是分配 2 个 long)。

所以而不是

 unsigned long __##name##_ctx[(sizeof(coro_context)+size)/sizeof(unsigned long)]={0};

我认为应该是

 unsigned long __##name##_ctx[(sizeof(coro_context)+size+sizeof(unsigned long))/sizeof(unsigned long)]={0};

现在关于静态初始化,我不会使用长缓冲区,而是使用相同的前 n 个变量创建一个新结构,并在末尾使用请求大小的缓冲区。这个结构可以静态初始化,请求的指针将指向它。所以代码看起来像这样:

#define CORO_CONTEXT(name,size,my_private_method) \
typedef struct coro_context##_name {\
    int id;\
    jmp_buf env;\
    list_head list;\
    jmp_buf waiter;\
    long timeout;\
    void *private;\
    char stack[0];\
    char additional_buffer[size];\
} coro_context##name;\
coro_context##name __name##_ctx = {0,0,0,0,0,my_private_method};\
coro_context *name##_ctx = (coro_context *)&__name##_ctx

为了简单起见,您还可以使用简化结构检查此代码,并使用 coro_context 结构作为包装器结构的 "base"(即单个变量而不是声明所有 coro_context变量):

 typedef struct coro_context {
     int id;
     void *private;
 } coro_context;

#define DECLARE_CORO_CONTEXT(size,private_method)\
 typedef struct wrapper_for_core_context\
 {\
    coro_context base;\
    char buffer[size];\
 }wrapper;\
 wrapper my_wrapper = { {0, private_method}, 0 };\
 coro_context *cc = (coro_context *)&my_wrapper;


//now the using code
    DECLARE_CORO_CONTEXT( 20, test_at_avro_bytes_field_all_data_values );

我会使用一个联合来覆盖 struct coro_context 带有字符缓冲区的实例:

#include <setjmp.h>
typedef struct coro_context {
    int id;
    jmp_buf env;
    list_head list;
    jmp_buf waiter;
    long timeout;
    void *private;
    char stack[]; /*should be [] in C11, [0] is not valid C */
} coro_context;

/*shouldn't start globals with underscore*/
/*shouldn't violate aliasing rules*/
/*C11 compound literals obviate the need for an extra global identifier*/
#define CORO_CONTEXT(name,size) \
    coro_context *name##_ctx = &(union { coro_context ctx; char buf[sizeof(coro_context)+size]; }){ .ctx={ .private=&name } }.ctx;

int my_name;

CORO_CONTEXT(my_name,32)

然后取.ctx的地址。这也应该消除您的解决方案存在的别名问题。

您需要使用全局地址进行初始化,因为全局值在静态初始化中不可用。

如果你想要一些与 C99 兼容的东西,你将需要额外的标识符,但它不应该以两个下划线开头:

#include <setjmp.h>
typedef struct coro_context {
    int id;
    jmp_buf env;
    list_head list;
    jmp_buf waiter;
    long timeout;
    void *private;
    char stack[1]; /*c99 doesn't have flexible array members*/
} coro_context;

#define CORO_CONTEXT(name,size) \
    union { coro_context ctx; char buf[sizeof(coro_context)+size]; } name##_ctx__ = { { 0, {0},{0},{0}, 0, &name } }; \
    coro_context *name##_ctx = &name##_ctx__.ctx;

int my_name;

CORO_CONTEXT(my_name,32)