10 Comments
⭠ Return to thread

Thank for this post, I just discover the blog !

I personally use an different form similar to X macro, but I can add any number of parameter without the need of changing everything. And the way I use them is a tiny bit more flexible.

Here is a short (modified for the example) portion of code for an interpreter I wrote in C,

My ways of making extensible enum (enum with more data attach to it) look like:

```c

#define category_int 'i'

#define category_float 'f'

// defining the enum named bi (build in) : (c type, type category)

#define i32_bi (int32_t , category_int)

#define i64_bi (int64_t , category_int)

#define f32_bi (float, category_float)

#define f64_bi (double, category_float)

// each member of the enum should have the same number of value

// you can do add more data attach to it like: (int32_t , category_int, "INT", 1)

```

Getting the info you need about an bi :

```c

#define get_1(a, b) a

#define get_2(a, b) b

#define bi_type(bi) get_1 bi

#define bi_category(bi) get_2 bi

```

so things like `bi_category(f64_bi)` -> `category_float` === `'f'` work

Mapping enum to stuff (function, macro...) :

```c

//build_in = (integer_type) Union (float_type)

// map the macro on all integer type inside bi :

#define map_on_bi_integer(macro)\

macro(i32_bi)\

macro(i64_bi)

#define map_on_bi_float(macro)\

macro(f32_bi)\

macro(f64_bi)

#define map_on_bi(macro)\

map_on_bi_integer(macro)\

map_on_bi_float(macro)

```

And then map it to whatever ! You can generate enum, switch case, custom function body and definition...

```c

// glue without evaluating the macro a and b

#define glue_instant(a,b) a ## b

// eval the macro FIRST, and then glue the result (other wise you will glue the unevaluated macro

#define glue(a,b) glue_instant(a, b)

enum bi_enum

{

// note : the ## glue macro don't directly work here, you need

#define declare_bi_enum_member(bi) glue(enum_,bi_type(bi)),

map_on_bi(declare_bi_enum_member)

/*

expend to :

enum_i32_bi, enum_i64_bi, enum_f32_bi, enum_f64_bi,

*/

#undef declare_bi_enum_member

}

Here is a switch case exemple :

```c

bool get_category(bi_enum b)

{

switch(b)

{

#define handle_case(bi)\

case glue(enum_, bi): return bi_category(bi); break;

/*break is not really helpful but anyways*/

map_on_bi(handle_case)

default: TODO; break; // (some macro to crash and tell the case was not implemented)

}

}

// or you can define a global array of bool if you want

```

the `map_on...` macro are great to map stuff, especially because you can create different family like :

```c

bool display(bi_enum b)

{

switch(b)

{

#define handle_int(bi)\

case glue(enum_, bi): printf("some integer!"); break;

#define handle_float(bi)\

case glue(enum_, bi): printf("some float!"); break;

/*break is not really helpful but anyways*/

map_on_bi_integer(handle_int)

map_on_bi_float(handle_float)

default: TODO; break;

}

}

```

I generally I put the enum number inside the define (`#define i32_bi (int32_t , category_int, 1)`), and add :

```

#define get_3(a, b, c) c

#define bi_id(bi) get_3 bi

```

so I don't declare an enum and the macro are a tiny bit easier to write.

But you can do it without it like in this example if you want.

(Fun fact, I'm a 21 years student in CS)

Expand full comment