C++ Preprocessor: Avoid Code Repetition of Member Variable List

C++ preprocessor: avoid code repetition of member variable list

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 x you will write (int) x. Here are some handy macros to help with typed expressions:

#define REM(...) __VA_ARGS__
#define EAT(...)

// Retrieve the type
#define TYPEOF(x) DETAIL_TYPEOF(DETAIL_TYPEOF_PROBE x,)
#define DETAIL_TYPEOF(...) DETAIL_TYPEOF_HEAD(__VA_ARGS__)
#define DETAIL_TYPEOF_HEAD(x, ...) REM x
#define DETAIL_TYPEOF_PROBE(...) (__VA_ARGS__),
// Strip off the type
#define STRIP(x) EAT x
// Show the type without parenthesis
#define PAIR(x) REM x

Next, we define a REFLECTABLE macro to generate the data about each field(plus the field itself). This macro will be called like this:

REFLECTABLE
(
(const char *) name,
(int) age
)

So using Boost.PP we iterate over each argument and generate the data like this:

// A helper metafunction for adding const to a type
template<class M, class T>
struct make_const
{
typedef T type;
};

template<class M, class T>
struct make_const<const M, T>
{
typedef typename boost::add_const<T>::type type;
};

#define REFLECTABLE(...) \
static const int fields_n = BOOST_PP_VARIADIC_SIZE(__VA_ARGS__); \
friend struct reflector; \
template<int N, class Self> \
struct field_data {}; \
BOOST_PP_SEQ_FOR_EACH_I(REFLECT_EACH, data, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))

#define REFLECT_EACH(r, data, i, x) \
PAIR(x); \
template<class Self> \
struct field_data<i, Self> \
{ \
Self & self; \
field_data(Self & self) : self(self) {} \
\
typename make_const<Self, TYPEOF(x)>::type & get() \
{ \
return self.STRIP(x); \
}\
typename boost::add_const<TYPEOF(x)>::type & get() const \
{ \
return self.STRIP(x); \
}\
const char * name() const \
{\
return BOOST_PP_STRINGIZE(STRIP(x)); \
} \
}; \

What this does is generate a constant fields_n that is number of reflectable fields in the class. Then it specializes the field_data for each field. It also friends the reflector class, this is so it can access the fields even when they are private:

struct reflector
{
//Get field_data at index N
template<int N, class T>
static typename T::template field_data<N, T> get_field_data(T& x)
{
return typename T::template field_data<N, T>(x);
}

// Get the number of fields
template<class T>
struct fields
{
static const int n = T::fields_n;
};
};

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:

struct field_visitor
{
template<class C, class Visitor, class T>
void operator()(C& c, Visitor v, T)
{
v(reflector::get_field_data<T::value>(c));
}
};

template<class C, class Visitor>
void visit_each(C & c, Visitor v)
{
typedef boost::mpl::range_c<int,0,reflector::fields<C>::n> range;
boost::mpl::for_each<range>(boost::bind<void>(field_visitor(), boost::ref(c), v, _1));
}

Now for the moment of truth we put it all together. Here is how we can define the Person class:

struct Person
{
Person(const char *name, int age)
:
name(name),
age(age)
{
}
private:
REFLECTABLE
(
(const char *) name,
(int) age
)
};

Here is the generalized print_fields function:

struct print_visitor
{
template<class FieldData>
void operator()(FieldData f)
{
std::cout << f.name() << "=" << f.get() << std::endl;
}
};

template<class T>
void print_fields(T & x)
{
visit_each(x, print_visitor());
}

An example:

int main()
{
Person p("Tom", 82);
print_fields(p);
return 0;
}

Which outputs:

name=Tom
age=82

And voila, we have just implemented reflection in C++, in under 100 lines of code.

C++ How to avoid repetitive code for variables of different third-party type?

It seems like this is exactly the use case for the preprocessor---removing repeating code and stringifying identifiers. If you don't like it, and you don't mind the horrible syntax, you could make Manager a tuple:

class Manager {
std::tuple<Type1, Type2, Type3 /* more types can be added ... */> tup;
}

Then you can run a function on each element using std::apply. See Template tuple - calling a function on each element.

void Manager::process() {
std::apply([](auto ...x){
std::make_tuple(process(x)...);
}, this->tup);
}

Without reflection I believe there is no cleaner solution. And your stringify print example is impossible without macros.

C Macro to remove duplicates in list of arguments

If your arguments are always small decimal numbers as in your example, you could get away with what I provide in P99. It has macros like P99_IF_EQ that you could use as

#define MAC(A,B) unsigned P99_PASTE2(myvar_, A) P99_IF_EQ(A,B)()(; unsigned P99(unsigned P99_PASTE2(myvar_, B))

MAC(1,2); // -> myvar_1 and myvar_2
MAC(3,3); // -> myvar_3

to only expand the declaration for B if it is not equal to A. Obviously for three different arguments this already becomes a bit tedious, but would be doable.

Reducing code repetition in C++ (or x-treme x-macros)

functional approach may solve many of your woes, but you should be aware that heavy use of preprocessor leads to code that is hard to debug. you're bound to spend much time formatting the code whenever there's a syntax error in the generated code (and you're bound to hit that when your macro use grows sufficiently); it'll also impact your mood when you need to use gdb or some such.

the following is obviously just a sketch to give you an idea.

#  define TELEPORTER_LUA_METHOD_TABLE(class_, item) \
item(class_, addDest, ARRAYDEF({{ PT, END }}), 1 ) \
item(class_, delDest, ARRAYDEF({{ INT, END }}), 1 ) \
item(class_, clearDests, ARRAYDEF({{ END }}), 1 ) \

# define LUA_METHOD_ITEM(class_, name, b, c) \
{ #name, luaW_doMethod<class_, &class_::name > },

# define LUA_FUNARGS_ITEM(class_, name, profiles, profileCount) \
{ #name, profiles, profileCount },

#define LUA_METHODS_TABLE(class_, table) \
const luaL_reg class_::luaMethods[] = \
{ \
table(class_, LUA_METHOD_ITEM) \
{ NULL, NULL } \
};

#define LUA_FUNARGS_TABLE(class_, table) \
const LuaFunctionProfile class_::functionArgs[] = \
{ \
table(class_, LUA_FUNARGS_ITEM) \
{ NULL, { }, 0 } \
};

LUA_METHODS_TABLE(Teleporter, TELEPORTER_LUA_METHOD_TABLE)

LUA_FUNARGS_TABLE(Teleporter, TELEPORTER_LUA_METHOD_TABLE)

#undef TELEPORTER_LUA_METHOD_TABLE

edit to answer Watusimoto's question from the comments.

Watusimoto proposes something like this:

#define LUA_METHODS_TABLE(class_) \
const luaL_reg class_::luaMethods[] = \
{ \
LUA_METHOD_TABLE(class_, LUA_METHOD_ITEM) \
{ NULL, NULL } \
};

#define LUA_FUNARGS_TABLE(class_, table) \
const LuaFunctionProfile class_::functionArgs[] = \
{ \
LUA_METHOD_TABLE(class_, LUA_FUNARGS_ITEM) \
{ NULL, { }, 0 } \
};

#ifdef LUA_METHOD_TABLE
# undef LUA_METHOD_TABLE
#endif

# define LUA_METHOD_TABLE(class_, item) \
... class-specific definition ...

LUA_METHODS_TABLE(Teleporter)
LUA_FUNARGS_TABLE(Teleporter)

The drawback to this is that it's not clear how is LUA_METHOD_TABLE related to the two macro calls that follow. It's just as if sprintf(3) didn't take arguments and instead expected the data in global variables of specific names. From the comprehensibility point of view it's better for any piece of code to be explicit about its immediate inputs, things it works on and which are different between its uses. But the global table macro also loses on the composability front: the global macro precludes production of multiple class definitions in one go, eg. with BPP or similar.

Detect C/C++ preprocessor abuse that leads to huge expanded code sizes

As far as I know there is no way to do what you are trying to achieve with a simple gcc command.
A way around it could be too add some extra steps into your build system, to see if a commit increased the code base by a certain percentage.

You have an option (-E) in gcc that outputs the code after the pre-processor step.

If you have a build at commit 1, you could save the number of lines at that commit, running gcc with -E, and doing a "wc -l" on its output.

At commit 2, before building, you do the same, and check that the number of lines hasn't increased by a threshold that you defined (10%, 20%?)

Can I use a bit of code as a parameter into a C++ template? (Without adding a function call)

why not:

#include <stdio.h>
int a, b, c;

template <int N>
void func(){
a = 1;
if(N==1){
c = 3;
#include "code1.inl"
}else if(N==2){
c = 999;
#include "code2.inl"
}
b = 2;
}

int main(){
printf("a=%d;b=%d;c=%d\n", a, b, c);
func<1>();
printf("a=%d;b=%d;c=%d\n", a, b, c);
func<2>();
printf("a=%d;b=%d;c=%d\n", a, b, c);
func<3>();
printf("a=%d;b=%d;c=%d\n", a, b, c);
return 0;
}

gives:

a=0;b=0;c=0
a=1;b=2;c=3
a=1;b=2;c=999
a=1;b=2;c=999

Check:
if you compile with gcc -O0, you can see the functions in with "nm"

00000000000011fb W _Z4funcILi1EEvv
0000000000001224 W _Z4funcILi2EEvv
000000000000124d W _Z4funcILi3EEvv

But if you compile it with -O1, you don't see them anymore, so they are optimized out.



Related Topics



Leave a reply



Submit