Это очень, очень уродливо, но я могу думать только о X-Macro:
#include <stdio.h>
#define MEMBERS \
X(char a) \
X(float b) \
X(double c)
struct T {
#define X(x) x;
MEMBERS
#undef X
};
int main(void)
{
size_t n = 0
#define X(x) +1
MEMBERS
#undef X
;
printf("%zu members\n", n);
return 0;
}
Это расширяется до:
#include <stdio.h>
struct T {
char a;
float b;
double c;
};
int main(void)
{
size_t n = 0+1+1+1;
printf("%zu members\n", n);
return 0;
}
Как указано @ HAL9000 в комментариях, вы можете одновременно сгенерировать и (struct
, и счетчик) следующим образом:
#include <stdio.h>
#define GENERATE_STRUCT(s) s;
#define GENERATE_SCOUNT(s) +1
#define set_struct(name) \
struct name { \
name##_members(GENERATE_STRUCT) \
}; \
\
size_t name##_count(void) \
{ \
return 0 \
name##_members(GENERATE_SCOUNT) \
; \
}
#define foo_members(X) \
X(char a) \
X(float b) \
X(double c)
set_struct(foo)
int main(void)
{
printf("%zu members\n", foo_count());
return 0;
}
В этом случае расширяется до:
#include <stdio.h>
struct foo {
char a;
float b;
double c;
};
size_t foo_count(void)
{
return 0+1+1+1;
}
int main(void)
{
printf("%zu members\n", foo_count());
return 0;
}
Наконец, другая реализация, предоставленная @Lundin, использует enum
вместо переменной:
#include <stdio.h>
#define MEMBERS \
/* type name */ \
X(char, a) \
X(float, b) \
X(double, c) \
struct T {
#define X(type, name) type name;
MEMBERS
#undef X
};
enum
{
#define X(type, name) dummy_##name,
MEMBERS
#undef X
MEMBER_COUNT
};
int main(void)
{
printf("%d members\n", MEMBER_COUNT);
return 0;
}
Расширяется до:
#include <stdio.h>
struct T {
char a;
float b;
double c;
};
enum {
dummy_a,
dummy_b,
dummy_c,
MEMBER_COUNT
};
int main(void)
{
printf("%d members\n", MEMBER_COUNT);
return 0;
}