Skip to content

Instantly share code, notes, and snippets.

@jdah
Created January 31, 2024 14:21
Show Gist options
  • Save jdah/1ae0048faa2c627f7f5cb1b68f7a2c02 to your computer and use it in GitHub Desktop.
Save jdah/1ae0048faa2c627f7f5cb1b68f7a2c02 to your computer and use it in GitHub Desktop.
explaining some C macro magic
// so a cool trick with macros in C (and C++) is that since macros inside of
// macros are stille evaluated by the preprocessor, you can use macro names as
// parameters to other macros (and even construct macro names out of out of
// parameters!) - so using this trick if we have some macro like
// this:
#include <stddef.h>
#define MY_TYPES_ITER(_F, ...) \
_F(FOO, foo, 0, __VA_ARGS__) \
_F(BAR, bar, 1, __VA_ARGS__) \
_F(BAZ, baz, 2, __VA_ARGS__) \
// then we can first use this trick to create an enum with the members
// MY_TYPE_FOO, MY_TYPE_BAR, MY_TYPE_BAZ
#define DECL_TYPES_ENUM_MEMBER(uc, lc, i, ...) \
MY_TYPE_##uc = i,
typedef enum my_type {
MY_TYPES_ITER(DECL_TYPES_ENUM_MEMBER)
} my_type_e;
// and this expands to
//
// enum my_types {
// MY_TYPE_FOO = 0,
// MY_TYPE_BAR = 1,
// MY_TYPE_BAZ = 2,
// };
//
// which can be pretty useful - better yet though, if we have all of our types
// defined...
typedef struct foo { int x; } foo_t;
typedef struct bar { float y; } bar_t;
typedef struct baz { const char *z; } baz_t;
// then we can *also* use this trick to automatically make a big ol' union of
// that type *using* the enum as the type descriminator
typedef struct {
union {
#define DECL_TYPES_UNION_MEMBER(uc, lc, ...) lc##_t lc;
MY_TYPES_ITER(DECL_TYPES_UNION_MEMBER)
};
my_type_e type;
} my_types_t;
// and now my_types_t has a union of a foo_t, bar_t, and baz_t
// pretty cool, right?
// so *even better* than this, if we also list out the fields on each type
// (you should probably do this on each struct definition but we'll do it here
// for the sake of chronology)...
#define FOO_FIELDS(_F, ...) \
_F(x, __VA_ARGS__)
#define BAR_FIELDS(_F, ...) \
_F(y, __VA_ARGS__)
#define BAZ_FIELDS(_F, ...) \
_F(z, __VA_ARGS__)
// and then we use _Generic to map types to another enum...
typedef enum field_type {
FIELD_TYPE_INT,
FIELD_TYPE_FLOAT,
FIELD_TYPE_STR,
// ... (fill out as you need)
} field_type_e;
#define TYPE_TO_FIELD_TYPE(x) _Generic(*((x*) NULL), \
int: FIELD_TYPE_INT, \
float: FIELD_TYPE_FLOAT, \
const char*: FIELD_TYPE_STR \
)
// and then we define structs to contain information about fields...
typedef struct field_desc {
field_type_e type;
const char *name;
int offset, size;
} field_desc_t;
typedef struct type_desc {
field_desc_t fields[16];
} type_desc_t;
// then we can use the above macros to autogenerate type info (!) which can be
// used for all sorts of stuff, including serialization!
#define TYPE_OF_FIELD(parent, field) __typeof__(((parent*) NULL)->field)
#define DO_FIELD_DESC(fname, pname) \
{ \
.type = TYPE_TO_FIELD_TYPE(TYPE_OF_FIELD(pname, fname)), \
.name = #fname, \
.size = sizeof(TYPE_OF_FIELD(pname, fname)), \
.offset = offsetof(pname, fname), \
},
#define DO_TYPE_DESC(uc, lc, i, ...) \
[i] = { .fields = { uc##_FIELDS(DO_FIELD_DESC, lc##_t) } },
// this array contains all type info for each field on each of our structs!
// automatically !!!!
const type_desc_t types[] = {
MY_TYPES_ITER(DO_TYPE_DESC)
}
@weeniemount
Copy link

h

@weeniemount
Copy link

h

@yakkudev
Copy link

this is what C does to a man

@advent94
Copy link

advent94 commented Feb 1, 2024

I C

@Bricktech2000
Copy link

@jdah you should write a C compiler someday. bonus points if it doesn't choke on this C preprocessor incantation

@tomerab1
Copy link

tomerab1 commented Feb 1, 2024

🥴🥴🥴

@KorigamiK
Copy link

h

h

@DanyaIzm
Copy link

DanyaIzm commented Feb 1, 2024

Seems like you've invented a new programming paradigm: "Macro-oriented programming"

@wienski
Copy link

wienski commented Feb 2, 2024

Seems like you've invented a new programming paradigm: "Macro-oriented programming"

This stuff is like half a century old.

@sleeptightAnsiC
Copy link

This is very similar to X macros

@kutaycinar
Copy link

IMG_8224

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment