Conditional X-MACROs to align ENUMs and String - c

I have a list of enums:
typedef enum {
ENUM1,
ENUM2,
#if FLAG
ENUM3,
#endif
} enum_var_t;
And a corresponding list of strings to align:
typedef struct { char[50] name; int val; } name_val_map_t
name_val_map_t name_val_map_table[] = {
{.name="string1", .val=ENUM1},
{.name="string2", .val=ENUM2},
#if FLAG
{.name="string3", .val=ENUM3},
#endif
};
FLAG is a build flag, and is either 0, or 1.
I am trying to use X-Macros to align these according to an answer here:
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, foo) IF_ ## cond (foo)
#define IF_0(foo)
#define IF_1(foo) foo
#define var_list \
X(ENUM1, "string1"), \
X(ENUM2, "string2"), \
IF(FLAG, X(ENUM3, "string3")), \
#define X(ENUMVAL, ...) ENUMVAL
typedef enum {
var_list
}
#undef X
#define X(ENUMVAL, NAME) {.name = NAME, .val = ENUMVAL}
name_val_map_t name_val_map_table = {
var_list
}
This leads to an error which says I'm passing more arguments to the IF macro than declared. I presume it is treating the comma inside the X(ENUM3, "string3") as an argument separator for IF.
I tried encapsulating the X() call with braces and removing the braces from IF_IMPL, but that didn't work either. If I try expand the argument list in IF() using ..., and VA_ARGS, I get expected expression errors. I'm trying to avoid using a def file as this makes my file unreadable. Some solution like I was trying would be perfect to avoid code replication, and for readability. Any pointers would be welcome, thanks!

Using variadic macros.
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__
Test:
//usr/local/bin/tcc -run "$0"; exit $?
#include <stdio.h>
#define FLAG3 1
#define FLAG4 0
#define FLAG5 1
typedef struct { char *name; int val; } name_val_map_t;
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, ...) IF_ ## cond(__VA_ARGS__)
#define IF_0(foo, ...)
#define IF_1(foo, ...) foo, __VA_ARGS__
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
IF(FLAG3, X(ENUM3, "string3")) \
IF(FLAG4, X(ENUM4, "string4")) \
IF(FLAG5, X(ENUM5, "string5")) \
typedef enum {
#define X(ENUMVAL, str) ENUMVAL,
var_list
#undef X
} enum_var_t;
name_val_map_t name_val_map_table[] = {
#define X(ENUMVAL, NAME) { NAME, ENUMVAL },
var_list
#undef X
{ "sentinel value", 99 }
};
int main(void){
int x =0;
while(name_val_map_table[x].val != 99){
printf("%i, %s\n", name_val_map_table[x].val, name_val_map_table[x].name);
x++;}
return 0;
}
/* output:
0, string1
1, string2
2, string3
3, string5
*/
Another option is to manually create IF_FLAGx( X(bla, bla) ) macros for every case...
See also: macro specialization based on argument in case of MSVC bug.

This seems needlessly complicated. I would simply do this instead:
#if FLAG
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
X(ENUM3, "string3")
#else
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2")
#endif
Full example:
#include <stdio.h>
#define FLAG 1
#if FLAG
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2") \
X(ENUM3, "string3")
#else
#define var_list \
X(ENUM1, "string1") \
X(ENUM2, "string2")
#endif
typedef enum
{
#define X(enum_var, str) enum_var,
var_list
#undef X
ENUM_N
} enum_var_t;
typedef struct
{
char name[50];
int val;
} name_val_map_t;
const name_val_map_t name_val_map_table[] =
{
#define X(enum_var, str) { .name = str, .val = enum_var },
var_list
#undef X
};
int main (void)
{
for(size_t i=0; i<ENUM_N; i++)
{
printf("%d %s\n", name_val_map_table[i].val, name_val_map_table[i].name);
}
}

Related

meet "called object is not a function" error when use macro to Initialization tree data

I want to implement the Lisp Cons type in C, so I use link list to achieve it.
At the same time, I use a lot of macros to simpilfy the data creation.
The code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Macro Utils
*/
#define create_m(type) ((type *) malloc(sizeof(type)))
#define new_m(type, ...) ({ \
type * __t = create_m(type); \
*__t = (type){ __VA_ARGS__ }; \
__t; })
/*
* Type Define
*/
typedef struct atom Atom;
enum ATOM_TYPES;
// Cons Type
typedef Atom * Cons[2];
#define car(c) (c[0])
#define cdr(c) (c[1])
#define new_cons_m(car, cdr) ({ \
Cons * __c = create_m(Cons); \
car(__c) = car; \
cdr(__c) = cdr; })
// Id Type
typedef char * Id;
#define new_id_m(id) (strdup(id))
// Container Type
enum ATOM_TYPES {
ATOM_CONS, ATOM_ID
};
struct atom {
enum ATOM_TYPES type;
union {
Cons value_cons;
Id value_id;
void * value;
};
};
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
#define new_atom_cons_m(car, cdr) ( \
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
#define new_atom_id_m(id) ( \
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
int main() {
printf("start program\n");
Atom * test = new_atom_cons_m(
new_atom_id_m("lisp"),
new_atom_cons_m(
new_atom_id_m("log"),
new_atom_id_m("say hello")
)
);
return 0;
}
And it failed to compile, with the following log:
arane.c: In function ‘main’:
arane.c:16:27: error: called object is not a function or function pointer
#define new_m(type, ...) ({ \
^
arane.c:18:18: note: in definition of macro ‘new_m’
*__t = (type){ __VA_ARGS__ }; \
^~~~~~~~~~~
arane.c:68:3: note: in expansion of macro ‘new_atom_m’
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
^~~~~~~~~~
arane.c:68:39: note: in expansion of macro ‘new_cons_m’
new_atom_m(ATOM_CONS, .value_cons = new_cons_m(car, cdr)) )
^~~~~~~~~~
arane.c:92:17: note: in expansion of macro ‘new_atom_cons_m’
Atom * test = new_atom_cons_m(
^~~~~~~~~~~~~~~
arane.c:66:29: note: in expansion of macro ‘new_m’
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
^~~~~
arane.c:70:3: note: in expansion of macro ‘new_atom_m’
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
^~~~~~~~~~
arane.c:93:5: note: in expansion of macro ‘new_atom_id_m’
new_atom_id_m("lisp"),
^~~~~~~~~~~~~
...and much more errors
I think it is because there are some expressions in function call's argument list. I am seeking for some way to solve it, I use gcc7.5 to finish my work, if needed, I can upgrade it.
-----------2022-01-24-14:15-----------
The problem is solved, with some twest in code, however, I still don't really understand how it was happend.
The code changed as below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* Macro Utils
*/
#define create_m(type) ((type *) malloc(sizeof(type)))
#define new_m(type, ...) ({ \
type * __t = create_m(type); \
*__t = (type){ __VA_ARGS__ }; \
__t; })
/*
* Type Define
*/
typedef struct atom Atom;
enum ATOM_TYPES;
// Cons Type
typedef Atom * Cons[2];
#define car(c) (c[0])
#define cdr(c) (c[1]) // deletes here
// Id Type
typedef char * Id;
#define new_id_m(id) (strdup(id))
// Container Type
enum ATOM_TYPES {
ATOM_CONS, ATOM_ID
};
struct atom {
enum ATOM_TYPES type;
union {
Cons value_cons;
Id value_id;
void * value;
};
};
#define new_atom_m(t, ...) (new_m(Atom, .type = t, __VA_ARGS__))
#define new_atom_cons_m(car, cdr) ( \
new_atom_m(ATOM_CONS, .value_cons[0] = car, .value_cons[1] = cdr) )
#define new_atom_id_m(id) ( \ // changes here
new_atom_m(ATOM_CONS, .value_id = new_id_m(id)) )
int main() {
printf("start program\n");
Atom * test = new_atom_cons_m(
new_atom_id_m("lisp"),
new_atom_cons_m(
new_atom_id_m("log"),
new_atom_id_m("say hello")
)
);
// Atom * test = new_atom_cons_m(NULL, NULL);
return 0;
}

how to define a c macro that returns a function name according to the argument?

What I currently have
#define _CMPLT8 _mm_cmplt_epi8 // int8_t
#define _CMPLT32 _mm_cmplt_epi32 // int32_t
What I want (something similar to the following code)
#define _CMPLT(T) ( \
if(sizeof(T)==1) return _mm_cmplt_epi8 \
else if(sizeof(T)==4) return _mm_cmplt_epi32 \
else #error \
)
How could I this code?
If you want to return a string based on a type I'd go for generics:
#define _CMPLT(T) \
_Generic( (T), \
char: "1", \
int: "4", \
default: "0")
int main(void) {
char a;
int b;
printf("%s%s\n", _CMPLT(a), _CMPLT(b));
}
But I feel you want to call functions depending on arg type, so in that case:
#define _CMPLT(X, Y) _Generic((X), \
int8_t: _mm_cmplt_epi8(X, Y), \
int32_t: _mm_cmplt_epi32(X, Y) \
)
int main(void) {
int8_t a = 0, b = 1;
int32_t c = 2, d = 3;
printf("%d%d\n", _CMPLT(a, b), _CMPLT(c, d));
}
If you really need to use strings and sizeof, and can use compound statements, would this work for you?
#include <stdio.h>
#define _CMPLT(T) ({ \
switch(sizeof(T)) { \
case 1: "1"; \
case 4: "4"; \
}; \
"0"; \
})
int main(void) {
printf("%s%s\n",
_CMPLT(char), _CMPLT(int));
}

Generating multiple lists from one C X-Macro

I have a list of command ENUMs that can either be a request type, or a response type.
typedef enum {
ENUM1,
ENUM2,
...
} command_t;
And, I was planning on using X Macros to generate its ENUM, as well as name-string mapping table as follows:
// commmands.def
X(ENUM1),
X(ENUM2),
// main.h
#define X(ENUMVAL, ...) ENUMVAL
typedef enum {
#include "commands.def"
} myenum_e;
#undef X
#define X(ENUMVAL, NAME) {.name = NAME, .val = ENUMVAL}
name_val_map_t name_val_map_table = {
#include "commands.def"
} mytable_t;
#undef
Additionally, I also need to generate two sub-lists, namely, a req_list and a rsp_list.
Is there a way I can keep just the one commands.def file and achieve this?
I was trying something on the lines of:
// commmands.def
// NAME, REQ, RSP
X(ENUM1, 1, 0),
X(ENUM2, 0, 1),
// main.h
#define IF(cond, foo) IF_IMPL(cond, foo)
#define IF_IMPL(cond, foo) IF_ ## cond (foo)
#define IF_0(foo)
#define IF_1(foo) foo
#define X(ENUMVAL, REQ, RSP) IFCOND(REQ, case: ENUMVAL)
void _is_req(myenum_e command) {
switch(command) {
#include commands.def
return 1;
default: return 0;
}
}
#undef X
#define X(ENUMVAL, REQ, RSP) IFCOND(RSP, case: ENUMVAL)
void _is_rsp(myenum_e command) {
switch(command) {
#include commands.def
return 1;
default: return 0;
}
}
#undef X
Is something like this possible? Thanks in advance!

Boost Preprocessor Recursion

I'm trying to create a 'C' macro (not C++) that will define and initialize static data.
For example:
STATIC_CONST_STRUCT
(
A, a,
MEMBER_DATA(CONST_STR, a, "Hello, a")
MEMBER_DATA(CONST_STR, b, "Hello, b")
MEMBER_STRUCT
(
C, c,
MEMBER_DATA(CONST_STR, d, "Hello, d")
MEMBER_DATA(CONST_INT, e, 1)
)
);
Would cause the 'C' preprocessor to create:
static const struct A
{
CONST_STR a;
CONST_STR b;
struct C
{
CONST_STR d;
CONST_INT e;
} c;
} =
{"Hello, a", "Hello, b", {"Hello, d", 1}};
I've tried to use the Boost Preprocessor
http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/
but I can't quite figure out how to make this work. My macros stop expanding. I suspect the recursive nature of the problem having an arbitrarily deep nesting is why.
The solutions I read regarding getting the preprocessor to recurse either don't work, or the description of how to get recursion to work isn't clear enough to implement a working solution.
Here's what I have so far:
#define MEMBER_DATA_TAG 0
#define MEMBER_STRUCT_TAG 1
#define MEMBER_TAG(MEMBER) BOOST_PP_SEQ_ELEM(0, MEMBER)
#define MEMBER_DATA_TYPE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(1, MEMBER_DATA)
#define MEMBER_DATA_NAME(MEMBER_DATA) BOOST_PP_SEQ_ELEM(2, MEMBER_DATA)
#define MEMBER_DATA_VALUE(MEMBER_DATA) BOOST_PP_SEQ_ELEM(3, MEMBER_DATA)
#define MEMBER_STRUCT_TYPE(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(1, MEMBER_STRUCT)
#define MEMBER_STRUCT_NAME(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(2, MEMBER_STRUCT)
#define MEMBER_STRUCT_MEMBER_SEQ(MEMBER_STRUCT) BOOST_PP_SEQ_ELEM(3, MEMBER_STRUCT)
#define MEMBER_DATA(TYPE, NAME, VALUE) ((MEMBER_DATA_TAG)(TYPE)(NAME)(VALUE))
#define MEMBER_STRUCT(TYPE, NAME, MEMBER_SEQ) ((MEMBER_STRUCT_TAG)(TYPE)(NAME)(MEMBER_SEQ))
#define IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM) BOOST_PP_EQUAL(MEMBER_TAG(MEMBER_SEQ_ELEM), MEMBER_STRUCT_TAG)
#define MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ) \
struct TYPE \
{ \
BOOST_PP_SEQ_FOR_EACH(MEMBER_ELEM_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ) \
} NAME
#define MEMBER_ELEM_DECLARE(_r, _data, MEMBER_SEQ_ELEM) \
BOOST_PP_IIF \
( \
IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_DECLARE \
( \
MEMBER_STRUCT_TYPE(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_NAME(MEMBER_SEQ_ELEM), \
MEMBER_STRUCT_MEMBER_SEQ(MEMBER_SEQ_ELEM) \
), \
MEMBER_DATA_DECLARE \
( \
MEMBER_DATA_TYPE(MEMBER_SEQ_ELEM), \
MEMBER_DATA_NAME(MEMBER_SEQ_ELEM), \
MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM) \
) \
);
#define MEMBER_DATA_DECLARE(TYPE, NAME, VALUE) TYPE NAME
#define MEMBER_VALUE_INIT(MEMBER_SEQ) \
BOOST_PP_SEQ_FOR_EACH_I(MEMBER_VALUE_INIT_DECLARE, BOOST_PP_EMPTY(), MEMBER_SEQ);
#define MEMBER_VALUE_INIT_DECLARE(_r, _data, i, MEMBER_SEQ_ELEM) \
BOOST_PP_COMMA_IF(i) \
BOOST_PP_IIF \
( \
IS_MEMBER_STRUCT(MEMBER_SEQ_ELEM), \
{MEMBER_VALUE_INIT(MEMBER_SEQ_ELEM)}, \
MEMBER_DATA_VALUE(MEMBER_SEQ_ELEM) \
)
#define STATIC_CONST_STRUCT(TYPE, NAME, MEMBER_SEQ) \
static const MEMBER_STRUCT_DECLARE(TYPE, NAME, MEMBER_SEQ) = \
{ \
MEMBER_VALUE_INIT(MEMBER_SEQ) \
}
Thanks.
It can be done without boost-preprocessor.
You don't actually need recursion. Just a loop to iterate over the macro arguments twice.
I took liberty of modifying the syntax a bit, to allow for commas in types and initializers, in case someone decides to use it with C++.
STATIC_CONST_STRUCT
(
A, a,
MEMBER_DATA(a, const char *) "Hello, a"
MEMBER_DATA(b, const char *) "Hello, b"
MEMBER_STRUCT
(
C, c,
MEMBER_DATA(d, const char *) "Hello, d"
MEMBER_DATA(e, int) 42
)
)
This expands to:
static const struct A
{
const char *a;
const char *b;
struct C
{
const char *d;
int e;
} c;
} a =
{
"Hello, a",
"Hello, b",
{
"Hello, d",
42,
},
};
Implementation:
#define STATIC_CONST_STRUCT(type_, name_, ...) \
static const struct type_ { \
END( LOOP_DECL_0 (__VA_ARGS__) ) \
} name_ = { \
END( LOOP_INIT_0 (__VA_ARGS__) ) \
};
#define MEMBER_DATA(name_, ...) )(var,name_,(__VA_ARGS__),
#define MEMBER_STRUCT(type_, name_, ...) )(open,type_ __VA_ARGS__ )(close,name_
#define IDENTITY(...) __VA_ARGS__
#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y
#define END(...) END_(__VA_ARGS__)
#define END_(...) __VA_ARGS__##_END
#define LOOP_DECL_0() LOOP_DECL_A
#define LOOP_DECL_A(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_B
#define LOOP_DECL_B(...) LOOP_DECL_BODY(__VA_ARGS__) LOOP_DECL_A
#define LOOP_DECL_0_END
#define LOOP_DECL_A_END
#define LOOP_DECL_B_END
#define LOOP_DECL_BODY(action_, ...) CAT(LOOP_DECL_BODY_, action_)(__VA_ARGS__)
#define LOOP_DECL_BODY_var(name_, type_, ...) IDENTITY type_ name_;
#define LOOP_DECL_BODY_open(type_) struct type_ {
#define LOOP_DECL_BODY_close(name_) } name_;
#define LOOP_INIT_0() LOOP_INIT_A
#define LOOP_INIT_A(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_B
#define LOOP_INIT_B(...) LOOP_INIT_BODY(__VA_ARGS__) LOOP_INIT_A
#define LOOP_INIT_0_END
#define LOOP_INIT_A_END
#define LOOP_INIT_B_END
#define LOOP_INIT_BODY(action_, ...) CAT(LOOP_INIT_BODY_, action_)(__VA_ARGS__)
#define LOOP_INIT_BODY_var(name_, type_, ...) __VA_ARGS__,
#define LOOP_INIT_BODY_open(type_) {
#define LOOP_INIT_BODY_close(name_) },
If used with C++, IDENTITY type_ should be wrapped in std::type_identity_t<...> to allow using types such as function pointers without typedefing them. void (*)() foo; is illegal, while std::type_identity_t<void (*)()> foo; is ok.

Variable arguements in macro using ellipsis

I want to define a macro accepting either 1 or 2 parameters. Both the parameters should be different type. How to use ellipsis and read the arguments passed?
Below is the sample:
void test(char *var2)
{
printf("%s\n",var2);
}
#define PRINT_STRING(...) ( if (!var1) test(var2) )
int main(int argc, _TCHAR argv[]) {
PRINT_STRING(TRUE);
PRINT_STRING(FALSE,"Hello, World!");
return 0;
}
This is known as a Variadic macro.
If your compiler supports __VA_ARGS__, you can do it like this:
#include <stdio.h>
#define NUM_ARGS__(X, \
N64,N63,N62,N61,N60, \
N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N
#define NUM_ARGS(...) \
NUM_ARGS__(0, __VA_ARGS__, \
64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_STRING_1(var) \
{ if (!(var)) {} }
#define PRINT_STRING_2(var, ...) \
{ if (!(var)) test(__VA_ARGS__); }
#define PRINT_STRINGN__(N, ...) \
PRINT_STRING_##N(__VA_ARGS__)
#define PRINT_STRINGN(N, ...) \
PRINT_STRINGN__(N, __VA_ARGS__)
#define PRINT_STRING(...) \
PRINT_STRINGN(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
void test(char* var2)
{
printf("%s\n", var2);
}
int main(void)
{
PRINT_STRING(1);
PRINT_STRING(0, "Hello, World!");
PRINT_STRING(1, "You can't see me!");
return 0;
}
Output:
Hello, World!
To do something like that you'd have to implement a series of macros, something like this
#include <stdbool.h>
#define PRINT_STRING0(X, Y) do { if (X && Y) test(Y); } while(false)
#define PRINT_STRING1(X, Y, ...) PRINT_STRING0(X, Y)
#define PRINT_STRING(...) PRINT_STRING1(__VA_ARGS__, 0, 0)
The last of them (user interface) adds a second or third argument of 0. PRINT_STRING1 then ignores all arguments that are more than 2. And PRINT_STRING0 then does the work.
Some more remarks:
be careful that a macro like you want to program it here can be place syntactically just as any other statement. In the example here the do { } while(false) does that trick
since C99 has Boolean type and constants, these are _Bool or bool and false and true
Don't use a macro to do that, use a variadic function!
void print_string( bool should_print, ... )
{
if( should_print )
{
va_list argp;
va_start( argp, should_print);
char *string = va_arg(argp, char *);
if( string) printf("%s", string );
va_end( argp );
}
}
But be very careful when using these sorts of things, because va_arg doesn't check if you really have a 2nd argument.
Also, to use a macro that accepts one or 2 arguments, use the GCC trick (at the very bottom of this page):
#define PRINT_STRING( should , args... ) print_string( should , ##args )
(note the spaces between the commas)

Resources