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!
Related
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;
}
I find myself doing the following pattern quite a bit in C:
enum type {String, Number, Unknown};
const char* get_token_type(int type) {
switch (type) {
case String: return "String";
case Number: return "Number";
default: return "Unknown";
}
}
Are there any nice alternatives where I can sort of wrap the two up in one?
Here's one more way, it uses a global and not sure how 'clean' it is but at least I can set up everything up at the top in one copy-paste job.
enum type {String, Number, Unknown};
char type_string[][40] = {"String", "Number","Unknown"};
const char* get_token_type(int type) {
return type_string[type];
}
The ugly and slow switch solution should be replaced by a readable a look-up table:
const char* const type_string[] = {"String", "Number","Unknown"};
typedef enum {String, Number, Unknown, type_size} type;
...
type something = ...;
if(something > 0 && something<type_size)
puts( type_string[something] );
A less recommended but possible solution is "X macros". They are hard to read and mostly used when you have no option to change a program that you are maintaining, in order to centralize all data to a single place in the program.
It would look like this:
#include <stdio.h>
#define TYPE_LIST(X) \
X(String) \
X(Number) \
X(Unknown) \
typedef enum
{
#define X(x) x,
TYPE_LIST(X)
#undef X
type_size
} type;
const char* const type_string[] =
{
#define X(x) [x] = #x, // array designated initializer using enum constant as index
TYPE_LIST(X)
#undef X
};
_Static_assert(sizeof type_string/sizeof *type_string == type_size,
"type and type_string mismatch");
int main (void)
{
printf("%d %d %d\n", String, Number, Unknown);
printf("%s %s %s\n", type_string[String],
type_string[Number],
type_string[Unknown]);
// or if you really like X macros...
#define X(x) printf("%d is %s\n", x, #x);
TYPE_LIST(X)
#undef X
}
(This only works in standard C, not C90, due to the trailing comma in enum language defect in C90)
Here is a set of macros you can use to access and enumerate the enumeration names and/or values:
#include <stdio.h>
#include <string.h>
#define ENUM_DEF(e, ...) e __VA_ARGS__,
#define ENUM_VAL(e, ...) e,
#define ENUM_STR(e, ...) #e,
#define ENUM_CASE(e, ...) case e: return #e;
#define ENUM_COUNT(e, ...) +1
/* this macro contains the names and values of the enumeration members */
#define type_ENUMERATE(V) V(String,) V(Number, = 2) V(Unknown,)
/* the actual enum type definition */
enum type { type_ENUMERATE(ENUM_DEF) };
/* array type_value contains the values in the enumeration */
enum type type_value[] = { type_ENUMERATE(ENUM_VAL) };
/* array type_value contains the names of the enumeration members */
const char *type_name[] = { type_ENUMERATE(ENUM_STR) };
/* type_count is the number of elements in the enumeration */
#define type_count (ENUM_COUNT(e, ...))
const char *get_token_type(int type) {
switch (type) {
/* can only use a switch if all enumeration values are distinct */
type_ENUMERATE(ENUM_CASE);
default:
return "Error";
}
}
int main() {
printf("The type names and values are:\n");
/* format %d might be inappropriate if the enumeration values are too large */
for (int i = 0; i < type_count; i++) {
printf(" %s: %d\n", type_name[i], type_value[i]);
}
printf("String: %s -> %d\n", get_token_type(String), String);
printf("Number: %s -> %d\n", get_token_type(Number), Number);
printf("Unknown: %s -> %d\n", get_token_type(Unknown), Unknown);
printf("-1: %s -> %d\n", get_token_type(-1), -1);
return 0;
}
This works only for enums which are continously enumerated. If you have enums like V1 = 2, V2 = 5, ... it will not work, or you would have to manullay count them.
In this case you will get a compiletime error when you add or remove an enum, and you forgot to add/remove the name string. If you rename an enum you will get also an error, because the previous name no longer exits.
#include <stdio.h>
#include <string.h>
#define xstr(s) TOSTRING(s)
#define TOSTRING(s) #s
typedef enum
{
VALUE1
,VALUE2
,VALUE3
,VALUE_MAX
} Values;
typedef struct namemap
{
const char *name;
Values enumValue;
} NameMap;
const NameMap valueNames[] =
{
{ TOSTRING(VALUE1), __COUNTER__ }
, { TOSTRING(VALUE2), __COUNTER__ }
, { TOSTRING(VALUE3), __COUNTER__ }
};
static_assert((__COUNTER__) == VALUE_MAX, "Name missing in enum name array");
int main()
{
printf("%s\n", valueNames[VALUE1].name);
return 0;
}
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);
}
}
I am looking for a fancy way to link function pointers and enums.
In my case I have a message queue that holds a event id and some data associated with the event.
some simple pseudo code:
event=(eid, data)
switch(eid) {
case eid1:
handler1(data);
break;
case edi2:
handler2(data);
break;
}
Now I like to do some optimization. If the event id has the value of the function called inside of the switch case statement I can save the switch case decode by preserving a nice readability of the code.
event=(eid, data)
eid(data)
Now if I am putting it into an example like:
static void abc(void * p) {
}
static void abc2(void * p) {
}
enum eventId {
eid1 = abc,
eid2 = abc2
} xyz;
My compiler tells:
error: enumerator value for 'eid1' is not an integer constant eid1 = abc
What is absolutely right.
Any ideas how to solve that problem?
Use an array of function pointers, and use the enum as the index.
typedef void (*handler_func)(void *);
handler_func event_handlers[] = { abc, abc2 };
enum eventId {
eid1 = 0,
eid2 = 1,
eid_max
}
if (eid < eid_max) event_handlers[eid](data);
enums cannot be linked with other data in C, but the preprocessor can generate code for you in the form of X-Macros.
#include <stdio.h>
typedef void (*handler_func)(void *);
static void handler1(void *const param) {
printf("Event 1: %p.\n", param);
}
static void handler2(void *const param) {
printf("Event 2: %p.\n", param);
}
#define EVENT(X) \
X(EID1, &handler1), \
X(EID2, &handler2)
#define PARAMA(A, B) A
#define PARAMB(A, B) B
#define STRINGISEA(A, B) #A
enum Event { EVENT(PARAMA) };
static const handler_func event_handlers[] = { EVENT(PARAMB) };
static const char *const event_strings[] = { EVENT(STRINGISEA) };
/* Everything will be the same size, pick one. */
static const size_t event_size = sizeof event_strings / sizeof *event_strings;
int main(void) {
size_t i;
void *const param = (void *)0x100;
for(i = 0; i < event_size; i++) {
printf("Calling %s.\n", event_strings[i]);
event_handlers[i](param);
}
return 0;
}
Gives,
Calling EID1.
Event 1: 0x100.
Calling EID2.
Event 2: 0x100.
The advantage of this implementation is it's a single source of truth; if one decided to add more events, they will only need to be added in one spot. The disadvantage is it's hard to read.
As an extension to the answer of #Barmar, you can use a technique called X macro, to keep corresponding (eid, handler) pairs in order. Note that you need only to change the definition of LIST_OF_EVENTS macro, adding or deleting pairs as needed.
void handler1(void*);
void handler2(void*);
void handler3(void*);
#define LIST_OF_EVENTS X(eid1, handler1), X(eid2, handler2), X(eid3, handler3)
#define X(id, x) id
enum evID { LIST_OF_EVENTS };
#undef X
#define X(x, handler) handler
void (*handlers[])(void*) = { LIST_OF_EVENTS };
#undef X
int get_event(void**);
void event_loop(void)
{
for (;;) {
void *data;
int eid = get_event(&data);
handlers[eid](data);
}
}
Macro defitions expand to
enum evID { eid1, eid2, eid3 };
void (*handlers[])(void*) = { handler1, handler2, handler3 };
I am trying to simulate Switch statement using macros in my Header file (.h) .
I have some predefined macros:
#define MULTIPLY_BY_1 1
#define MULTIPLY_BY_10 2
#define MULTIPLY_BY_100 3
#define MULTIPLY_BY_1000 4
#define CHOSEN_FACTOR MULTIPLY_BY_100
I have a const result that takes a value according to CHOSEN_FACTOR (The user will define this macro). I am in the header file and I want to "simulate" the switch statement like this:
switch(CHOSEN_VALUE)
{
case MULTIPLY_BY_1:
const uint16_t result = 5;
break;
case MULTIPLY_BY_10:
const uint16_t result = 50;
break;
case MULTIPLY_BY_100:
const uint16_t result = 500;
break;
case MULTIPLY_BY_1000:
const uint16_t result = 50000;
break;
default:
break;
}
EDIT:
In the source file (.c), I want to use result like this:
uint16_t foo(void)
{
uint16_t myFoo = getMyFooValue();
return result * myFoo;
}
Is there any macros-based-solution to so ? Is there a more optimised approach to get the same result ?
Like in this answer suggests, i would suggest you to use enum for getting the selected value and based on the selection with the enum type, return the macro that you have specified. To do that, you can create a function and get the correct macro as shown below.
typedef enum {
MULTIPLY_BY_1
MULTIPLY_BY_10
MULTIPLY_BY_100
MULTIPLY_BY_1000
}multiplier_t;
uint16_t foo(multiplier_t multiplier)
{
switch (multiplier) {
case MULTIPLY_BY_1:
return 1;
case MULTIPLY_BY_10:
return 2;
case MULTIPLY_BY_100:
return 3;
case MULTIPLY_BY_1000:
return 4;
}
return 0; // just in case no code matches
}
Hope this helps you.
Assuming I understand what you're asking for, I think you're looking for something like this:
#define INIT_FACTOR(var, value) \
#ifdef CHOSEN_FACTOR \
#if CHOSEN_FACTOR == MULTIPLY_BY_1 \
const uint16_t var = value \
#elif CHOSEN_FACTOR == MULTIPLY_BY_10 \
const uint16_t var = value * 10 \
#elif CHOSEN_FACTOR == MULTIPLY_BY_100 \
const uint16_t var = value * 100 \
#elif CHOSEN_FACTOR == MULTIPLY_BY_1000 \
const uint16_t var = value * 1000 \
#endif \
#else \
const uint16_t var = value \
#endif
This will define a macro named INIT_FACTOR that takes two arguments, the name of the variable to define and the starting value. You'd add it in the body of your code as
INIT_FACTOR(result, 5);
and then, if CHOSEN_FACTOR is MULTIPLY_BY_100, that line would expand to
const uint16_t result = 5 * 100;
If CHOSEN_FACTOR is not defined, that line expands to
const uint16_t result = 5;
Remember that macro substitution happens at compile time, not run time. If you want a run time solution, this isn't it.
Selection in the preprocessor can be done by using a helper macro to expand a parameter and then pasting it with a token to kludge a look-up table:
#include <stdio.h>
int main(void)
{
#define MULTIPLY_BY_1 1
#define MULTIPLY_BY_10 2
#define MULTIPLY_BY_100 3
#define MULTIPLY_BY_1000 4
#define CHOSEN_FACTOR MULTIPLY_BY_100
#define Foo1 1
#define Foo2 10
#define Foo3 100
#define Foo4 1000
#define FooHelper(x) Foo##x
#define Foo(x) FooHelper(x)
printf("%d\n", Foo(CHOSEN_FACTOR));
}
Preprocessor abuse of this sort should be generally avoided and likely is not needed for the actual problem that motivated this question.
Thank you all for your answers. I was looking for a specific solution and I guess I found it.
#define MULTIPLY_BY_1 0
#define MULTIPLY_BY_10 1
#define MULTIPLY_BY_100 2
#define MULTIPLY_BY_1000 3
const struct
{
uint8_t index;
uint16_t value;
}myArray[] = {
{MULTIPLY_BY_1, 1},
{MULTIPLY_BY_10, 10},
{MULTIPLY_BY_100, 100},
{MULTIPLY_BY_1000, 1000}
};
#define CHOSEN_VALUE MULTIPLY_BY_10
const uint16_t result = myArray[CHOSEN_VALUE].value;
void foo(void)
{
printf("%d", result); // 10
}