Reputation: 1379
I want to have a macro
#define MY_STRUCT( /* ... /*) /* ... */
That I want to call this way
MY_STRUCT(point, double x, int y);
Which expands to this
typedef struct {
double x;
int y;
} point;
void init_point(point *p) {
p->x = load_double_from_somewhere();
p->y = load_int_from_somewhere();
}
The macro should be able to handle any number of parameters I give it. I'm trying to generate data bindings for model objects automatically through macros for I've been writing mapping functions by hand and it's tiresome and repetitive (xml to C struct). I know it's possible but I can't figure out how.
Upvotes: 3
Views: 2213
Reputation: 241971
Note: I'm only addressing the question of how to write the struct
definition. I don't see a way to get the preprocessor to automatically generate serialisation and deserialisation code, for a couple of reasons. First, it's not possible to extract a token --the member name-- from a macro argument, even if you could know which token to extract. Second, you could use C11's _Generic
feature to create calls to pre-defined type-specific serialisation/deserialisation functions. But the _Generic
call needs a complete list of possibilites, so it's not going to be possible to incrementally build up the possibilities. I think you'll find that existing systems which autogenerate serialisation/deserialisation code depend on external code generators. That's a much more flexible (and probably easier) strategy.
So, back to autogenerating struct declarations. As has been mentioned, this is really only possible if you generate N macros, one for every number of arguments. And you also need a way to compute N, which might require more automatically-generated macros. All in all, it's a lot easier to use a framework like Jens Gustedt's P99.
Here's a highly simplified illustration of how to do it, with a small limit of fields because the patterns are obvious.
We start with a macro which will compute the number of arguments in a variadic call. Note that this will note correctly return 0 if there are no variadic arguments. A robust solution, like P99, would handle that correctly. But in this case, it doesn't matter because a struct
with no members is not allowed by C, so we know there must be at least one argument. (That wouldn't be true in C++.)
#define ARG9_(a1, a2, a3, a4, a5, a6, a7, a8, a9, ...) a9
#define NARGS(...) ARG9_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
Now, we need eight macros which construct the members. All they have to do is insert semicolons between the arguments, so it's not very challenging:
#define S8_(first, ...) first; S7_(__VA_ARGS__)
#define S7_(first, ...) first; S6_(__VA_ARGS__)
#define S6_(first, ...) first; S5_(__VA_ARGS__)
#define S5_(first, ...) first; S4_(__VA_ARGS__)
#define S4_(first, ...) first; S3_(__VA_ARGS__)
#define S3_(first, ...) first; S2_(__VA_ARGS__)
#define S2_(first, ...) first; S1_(__VA_ARGS__)
#define S1_(first) first;
Finally, we need to put all that together. To do so, we're going to need a macro which can concatenate up a macro name:
#define PASTE3(a, b, c) PASTE3_(a, b, c)
#define PASTE3_(a, b, c) a ## b ## c
Finally, the macro which creates the struct
#define MY_STRUCT(name, ...) \
typedef struct name name; \
struct name { \
PASTE3(S, NARGS(__VA_ARGS__), _)(__VA_ARGS__) \
};
And now we can give it all a try:
MY_STRUCT(s1, int a);
MY_STRUCT(s2, int a, double b);
MY_STRUCT(s3, const char* s, int t[17], double sum);
MY_STRUCT(s4, char a, char b, char c, char d);
MY_STRUCT(s5, char a, char b, char c, char d, char e);
MY_STRUCT(s6, char a, char b, char c, char d, char e, char f);
MY_STRUCT(s7, char a, char b, char c, char d, char e, char f, char g);
MY_STRUCT(s8, char a, char b, char c, char d, char e, char f, char g, short h);
Here's what gcc -E
produces, given all of the above: (Note: I can't comment on whether this works on various versions of MSVC. But it's all standard C99.)
# 1 "nargs.h"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "nargs.h"
# 22 "nargs.h"
typedef struct s1 s1; struct s1 { int a; };
typedef struct s2 s2; struct s2 { int a; double b; };
typedef struct s3 s3; struct s3 { const char* s; int t[17]; double sum; };
typedef struct s4 s4; struct s4 { char a; char b; char c; char d; };
typedef struct s5 s5; struct s5 { char a; char b; char c; char d; char e; };
typedef struct s6 s6; struct s6 { char a; char b; char c; char d; char e; char f; };
typedef struct s7 s7; struct s7 { char a; char b; char c; char d; char e; char f; char g; };
typedef struct s8 s8; struct s8 { char a; char b; char c; char d; char e; char f; char g; short h; };
Upvotes: 2
Reputation:
If you want to autogenerate source code, the preprocessor is the worst tool you can get.
And you absolutely cannot create any macro to generate a struct with any number of fields; the C preprocessor does not support loops or recursion.
But if a maximum of 8 fields/arguments are enough, though NON-STANDARD, this will do in gcc and clang:
#define MY_STRUCT(name, ...) struct name { C2SC(__VA_ARGS__,,,,,,,,,) }
#define C2SC(a, b, c, d, e, f, g, h, ...) a; b; c; d; e; f; g; h;
MY_STRUCT(foo, int bar, int baz, void *quux, void *xuq, char sq[12]);
This deliberately creates a struct name { ... }
, not a typedef struct { ... } name
as in the OP: It's already obtuse enough even without a MY_STRUCT
macro which actually generates a typedef
.
Upvotes: 1
Reputation: 96924
I'm not sure if it's possible to extract x
from double x
(unless you know all possible types your variables can have).
Also, iterating over comma-separated lists using preprocessor requires writing boilerplate macros (O(n)
of them, I think). Boost.Preprocessor might help with that, but you can completely avoid boilerplate if you use a different syntax:
MY_STRUCT(point, (double,x)(double,y))
Implementation:
#define MY_STRUCT(name, seq) \
typedef struct { \
MY_STRUCT_impl_end(MY_STRUCT_impl_decl_loop_a seq) \
} name; \
void init_point(name *p) { \
MY_STRUCT_impl_end(MY_STRUCT_impl_load_loop_a seq) \
}
#define MY_STRUCT_impl_end(...) MY_STRUCT_impl_end_(__VA_ARGS__)
#define MY_STRUCT_impl_end_(...) __VA_ARGS__##_end
#define MY_STRUCT_impl_decl_loop(type, name) type name;
#define MY_STRUCT_impl_decl_loop_a(...) MY_STRUCT_impl_decl_loop(__VA_ARGS__) MY_STRUCT_impl_decl_loop_b
#define MY_STRUCT_impl_decl_loop_b(...) MY_STRUCT_impl_decl_loop(__VA_ARGS__) MY_STRUCT_impl_decl_loop_a
#define MY_STRUCT_impl_decl_loop_a_end
#define MY_STRUCT_impl_decl_loop_b_end
#define MY_STRUCT_impl_load_loop(type, name) p->name = load_##name##_from_somewhere();
#define MY_STRUCT_impl_load_loop_a(...) MY_STRUCT_impl_load_loop(__VA_ARGS__) MY_STRUCT_impl_load_loop_b
#define MY_STRUCT_impl_load_loop_b(...) MY_STRUCT_impl_load_loop(__VA_ARGS__) MY_STRUCT_impl_load_loop_a
#define MY_STRUCT_impl_load_loop_a_end
#define MY_STRUCT_impl_load_loop_b_end
Upvotes: 1
Reputation: 68089
I do not know what you need it for, but ...
#define x(a,b,c) struct a {b;c;}
x(mystr, int m, double n);
and the result is:
struct mystr {int m;double n;};
or
#define x(a,b,c) typedef struct {b;c;} a
x(mystr, int m, double n);
and the result
typedef struct {int m;double n;} mystr;
Upvotes: 1