Basically, instead of faffing around with undefing values and including different files, you define your list like this:
#define AN_X_LIST(X) \
X(foo, bar) \
X(bar, baz)
And then you use it like so:
#define AN_ASSIGNMENT_STATEMENT(a,b) a = STRINGIFY(b);
And so
AN_X_LIST(AN_ASSIGNMENT_STATEMENT)
Will expand to
foo = "bar";
bar = "baz";
The nice thing about this approach is you can define multiple lists and macros, and make higher order macros which use them. I have a system like this which allows me to define reflective structs in C++ easily, i.e. I define a struct like:
(where DECLARE_REFLECTIVE_STRUCT basically just does the same dance as above with passing different per-element structs into the list that it is passed for the struct definition, and other utility functions associated with it)
which then makes a struct Foo with members bar and baz with the right types and default values, but also I can do 'foo_instance.fetch_variant("baz")' and other such operations.
The biggest pain with this approach is it's basically dealing with a bunch of multi-line macros, so it can get messy if there's a typo somewhere (and I strongly recommend an auto-formatter if you don't like having a ragged line of backslashes to the right of all the code that uses it).