I have a multiple classes each with different member variables that are initialized trivially in a constructor. Here is an example:
struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
const char *name;
int age;
};
Each has an associated print<>() function.
template <>
void print<Person>(const Person &person)
{
std::cout << "name=" << name << "\n";
std::cout << "age=" << age << "\n";
}
This code is error prone since the parameter list is replicated in four places. How can I rewrite the code to avoid this duplication? I’d like to use the preprocessor and/or templates.
For example, could I use the X-args preprocessor technique — something like this?
#define ARGUMENTS \
ARG(const char *, name) \
ARG(int, age)
struct Person
{
Person(LIST_TYPE_NAME_COMMA(ARGUMENTS))
:
LIST_NAME_INIT(ARGUMENTS)
{
}
private:
LIST_TYPE_NAME_SEMICOLON(ARGUMENTS)
};
template <>
void print<Person>(const Person &person)
{
LIST_COUT_LINE(ARGUMENTS)
}
#undef ARGUMENTS
Or better, a template-based approach?
Please don’t question why I want to do this, there are reasoned design decisions that have resulted in multiple similar objects with named parameters. The parameters need to be named member variables for performance reasons. I’m just exploring whether it’s possible to list the parameters and their types only once.
What you need to do is have the preprocessor generate reflection data about the fields. This data can be stored as nested classes.
First, to make it easier and cleaner to write it in the preprocessor we will use typed expression. A typed expression is just an expression that puts the type in parenthesis. So instead of writing
int xyou will write(int) x. Here are some handy macros to help with typed expressions:Next, we define a
REFLECTABLEmacro to generate the data about each field(plus the field itself). This macro will be called like this:So using Boost.PP we iterate over each argument and generate the data like this:
What this does is generate a constant
fields_nthat is number of reflectable fields in the class. Then it specializes thefield_datafor each field. It also friends thereflectorclass, this is so it can access the fields even when they are private:Now to iterate over the fields we use the visitor pattern. We create an MPL range from 0 to the number of fields, and access the field data at that index. Then it passes the field data on to the user-provided visitor:
Now for the moment of truth we put it all together. Here is how we can define the
Personclass:Here is the generalized
print_fieldsfunction:An example:
Which outputs:
And voila, we have just implemented reflection in C++, in under 100 lines of code.