That's been my primary usage of them. Another is to create a list of config file options (or command line arguments) like
X(foo, int, "set the number of foos")
X(filename, std::string, "input filename")
X(verbose, bool, "verbose logging")
which can then be used to (a) generate the fields of a config struct, define the mapping from string to field (using the stringifying macro operators), define what functions to use for parsing each field, create the help message, etc. Basically like `argparse` or `clap` but much hackier.
As gross as they are, the ability to define one table of data that's used multiple ways in multiple places is handy.
As gross as they are, the ability to define one table of data that's used multiple ways in multiple places is handy.