1

I have a struct defined as:

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

which I need to initialize with three values

  • id = 0
  • private = function pointer
  • append to end of struct a suitable char array of length size

My current attempt looks like (the "unsigned long" bit is to get the right alignment):

#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

This works, but has two problems (ok, problems are one and a half ;) ):

  • it is UGLY (half problem).
  • I see no way to statically initialize private = name.

Note: I insist on "static initialization" because I want to use this in plain "C" (c11 is ok, if required) because I need to use these initialization outside function context.

ZioByte
  • 2,690
  • 1
  • 32
  • 68

2 Answers2

1

I'd use a union overlaying the struct coro_context instance with a char buffer:

#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)

and then take the address of .ctx. That should also get rid off the aliasing issues your solution has.

You'll need to initialize with an address of a global, because the value of a global is not usable in static initializations.

If you want something C99 compatible, you will need the extra identifier, but it shouldn't start with two underscores:

#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)
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • Thanks I got the same idea shortly after sending the question, but your code is much prettier (especially the c11 solution). A couple of questions, though: `char stack[0]` seems to be accepted by all compilers I have around (gscc, ghs, iar, ...); I really need the "`unsigned long`" trick because I use `stack` as a stack (unsurprisingly) and thus the *top* of it *must* be aligned (I *think* the best way to do it, across several architectures, is to alias to `long`, or should I use something like `void *`? – ZioByte Aug 19 '18 at 15:12
  • @ZioByte As for `char stack[0];`, why even tread there if you can avoid it? Invoking UB gives the compiler license to do anything. If you don't ever invoke it, it gives you license to complain to the compiler writers. I think the second alternative is a lot better. Regarding `unsigned long`, you don't need to break strict aliasing for the sake of alignment. C11 has `_Alignas` (`_Alignas(long) char stack[];`) and in older Cs, you can force an alignment with a union as well (maybe do it directly on the stack member `union{ char stack[1]; long stack_align_; }`). – Petr Skocik Aug 19 '18 at 15:22
  • Thanks PSkocik. Agreed on the first one. For alignment my problem is user could pass a `size` not a multiple of the alignment, since the "real" stack pointer will be set at *the end* of the array I need to guarantee it's `size` is correct, not just the start. – ZioByte Aug 19 '18 at 16:42
0

First note that the size of the long buffer, may not match what you expect,as it is rounded to floor. for example if core_context size is 10 bytes, and one requested additional size of 3 bytes, and assuming long size is 8 bytes. you get (10+3)/8 = 1, so you allocate 1 long for handling 13 bytes (instead of allocating 2 longs).

so instead of

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

I think it shall be

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

now regarding the static initialization, I wouldn't use long buffer, but create a new struct with the same first n variables, and with the requested sized buffer at end. this struct can be initialized statically, and the requested pointer will point to it. so the code would look like this:

#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

for sake of simplicity, you can examine also this code using simplified struct, and using the coro_context struct as "base" of the wrapper struct (i.e. single variable instead of declaring all coro_context variables):

 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 );
Eliyahu Machluf
  • 1,251
  • 8
  • 17