I need to have meta-information about structures in my code. So, I've code some combination of C structures (to store meta information) and C preprocessor macros to initialize these structures without much of boilerplate code. Now it looks like this (really, I'm storing a lot more information about fields, but this code is enough for my question):
#include <stdint.h>
#include <stdlib.h>
#include <stddef.h>
struct meta_field {
const char *name;
size_t offset;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field fields[];
};
#define META_STRUCT_BEGIN(NAME) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = {
#define META_STRUCT_END() ,{NULL, 0}}}
#define META_FIELD(NAME) { .name = "" #NAME, .offset = offsetof(struct CURRENT_META_STRUCT, NAME) }
struct example {
int i;
double d;
};
#define CURRENT_META_STRUCT example
META_STRUCT_BEGIN(CURRENT_META_STRUCT)
META_FIELD(i),
META_FIELD(d)
META_STRUCT_END();
#undef CURRENT_META_STRUCT
It works. But it has a lot of boilerplate still: #define CURRENT_META_STRUCT
, usage of CURRENT_META_STRUCT
as argument to META_STRUCT_BEGIN()
, #undef
for CURRENT_META_STRUCT
— all of this looks ugly, IMHO.
I know, that it is impossible to define macro by macro (so, META_STRUCT_BEGIN()
can not define CURRENT_META_STRUCT
). But looking at Branf*ck implementation in C Preprocessor and Boost-PP, I think that it is posisble to implement something which will look like this:
META_STRUCT(example,
META_FIELD(i),
META_FIELD(d)
)
But I can not wrap my head around this preprocessor magic.
Could somebody helps me?
I've read https://github.com/orangeduck/CPP_COMPLETE already, but it didn't help.
Also, it was marked as duplicate of this, but problem is I need to add "default" argument to all generated macro "calls".
Here are some sketch, what I want to achieve:
#define META_FIELD_IMPL(STRUCT, NAME) { .name = "" #NAME, .offset = offsetof(struct STRUCT, NAME) }
#define META_FIELD(NAME) NAME
#define META_STRUCT(NAME, ... ) \
static struct meta_struct s_meta_##NAME = { \
.name = "" #NAME, \
.size = sizeof(struct NAME), \
.fields = { \
/* Each ARG from __VA_ARGS__ is META_FIELD(x) and \
* I need to call META_FIELD_IMPL(NAME, <Expansion of META_FIELD>) \
* for each ARG from _VA_ARGS_ here */ \
{NULL, 0} \
}}
Now in this example there is only one argument for META_FIELD()
: NAME
, but in real system there are 6 arguments for META_FIELD()
and some helper macro which provide "default" values for common cases, so META_FIELD()
is necessary and could not be replaced by NAME
itself.
And one last complication: such META_STRUCT()
could be called as argument to META_FIELD()
, because some fields contains pointers to nested meta-structs! Now it is done by declaring named objects for all nested sub-structures, but I want to avoid it too! I understand, that depth of nesting could be limited by some arbitrary constant, it is Ok.
Update: I added this example of what I want to type and what I want to get after preprocessor. I can not figure out how to implement all these macros (marked with /* ??? */
). And, yes, I've checked, manually-coded "end result" compiles fine.
enum meta_type { mt_end, mt_int, mt_string, mt_struct };
struct meta_struct;
struct meta_field {
const char *name;
enum meta_type type;
size_t offset;
struct meta_struct *child;
};
struct meta_struct {
const char *name;
size_t size;
struct meta_field *fields;
};
#define META_STRUCT(NAME, ...) static struct meta_struct s_meta_##name = { .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
#define META_FIELD_IMPL0(STRUCT, NAME, TYPE, CHILD) { .name = #NAME, .type = TYPE, .offset = offsetof(STRUCT, NAME), .child = CHILD }
#define META_FIELD_IMPL1(NAME, TYPE, CHILD) /* ??? */
#define META_FIELD(NAME, TYPE) META_FIELD_IMPL1(NAME, TYPE, NULL)
#define META_FIELD_SUB(NAME, CHILD) META_FIELD_IMPL1(NAME, mt_struct, CHILD)
#define META_SUBSTRUCT(NAME, ...) (struct meta_struct){ .name = #NAME, .size = sizeof(struct NAME), .fileds = (struct meta_field[]){ /* ??? */, {NULL, mt_end, 0, NULL}}}
/* Example of "input": */
struct child {
int i;
};
struct parent {
int i;
struct child c;
const char *s;
};
META_STRUCT(parent,
META_FIELD(i, mt_int),
META_FIELD_SUB(c,
META_SUBSTRUCT(child,
META_FIELD(i, mt_int)
)
),
META_FIELD(s, mt_string)
);
/* This should give this */
static struct meta_struct s_meta_parent = {
.name = "parent",
.size = sizeof(struct parent),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct parent, i), .child = NULL },
{ .name = "c", .type = mt_struct, .offset = offsetof(struct parent, c), .child = &(struct meta_struct){
.name = "child",
.size = sizeof(struct child),
.fields = (struct meta_field[]) {
{ .name = "i", .type = mt_int, .offset = offsetof(struct child, i), .child = NULL },
{NULL, mt_end, 0, NULL}
}
}
},
{ .name = "s", .type = mt_string, .offset = offsetof(struct parent, s), .child = NULL },
{NULL, mt_end, 0, NULL}
}
};