boost contains a preprocessor metaprogramming libary. What this means, simply, is that it is possible to write code which generates code. The full docs are here.
It is possible to do full LISP-like lambda calculus with the library. That's not, however, where my interests lie. For me, the practical use is to automatically generate a set of templates which take N arguments and let the compiler generate all of the N iterations for you.
Here is a simple example. Say you want a set of template functions which convert any set of objects into their string representations using the boost::lexical_cast library.
#include <boost/preprocessor.hpp> #define rep(z, n, text) \ a.push_back(lexical_cast<string>(x ## n); #ifndef BOOST_PP_IS_ITERATING # ifndef CONVERTOBJECTS_HPP # define CONVERTOBJECTS_HPP //0 object case vector<string> convertObjsToStrings() { return vector<string>(); } #define BOOST_PP_ITERATION_LIMITS ( 1, 3) #define BOOST_PP_FILENAME_1 "convertobjects.h" #include BOOST_PP_ITERATE() # endif #else # define n BOOST_PP_ITERATION() template < BOOST_PP_ENUM_PARAMS(n, class A) > vector<string> convertObjsToStrings( BOOST_PP_ENUM_BINARY_PARAMS(n, A, x ) ) { vector<string> a; BOOST_PP_REPEAT(n, rep, ~) return a; } #endif
In the above code we have a few very simple concepts. First, we see that the code is going to iterate 3 times with the line: #define BOOST_PP_ITERATION_LIMITS ( 1, 3).
Second, we see that the header file includes itself with the line: #define BOOST_PP_FILENAME_1 "convertobjects.h".
Third, we accomplish the real work by iterating the rep macro define at the top of the file with the line: BOOST_PP_REPEAT(n, rep, ~)
This probably seems pretty abstract if you are anything like me. Below is the output from the C++ preprocessor, to show you the code that is generated:
template < class A0 > vector<string> convertObjsToStrings( A0 x0 ) { vector<string> a; a.push_back(lexical_cast<string>(x0); return a; } template < class A0 , class A1 > vector<string> convertObjsToStrings( A0 x0 , A1 x1 ) { vector<string> a; a.push_back(lexical_cast<string>(x0); a.push_back(lexical_cast<string>(x1); return a; } template < class A0 , class A1 , class A2 > vector<string> convertObjsToStrings( A0 x0 , A1 x1 , A2 x2 ) { vector<string> a; a.push_back(lexical_cast<string>(x0); a.push_back(lexical_cast<string>(x1); a.push_back(lexical_cast<string>(x2); return a; }
Of course you can increase the number of allowed parameters pretty high, I think up to 256 using the BOOST_PP_LIMIT_REPEAT macro.
Comments
bad example
Well, the Boost preprocessor metaprogramming library is definitely very useful, but I think your example is imho a bit unfortunately chosen and boost isn't the right thing for this. You'll get bloated code and you have a upper limit of arguments. And it's hard to read. ;)
Why not use the following for this task?
usage:
greetings, iliis
Yeah, good point
While it is true that some of the examples on this website are contrived (and I'm not an expert by any means) this particular one is based off of a real world problem that I was trying to solve. Namely: typesafe serialization of function calls for a distributed system.
In my system I have a templated function description that is utilized by both the sender and receiver to make sure that they can both stay in sync and follow code changes. A reduced example:
At other points in the code, a remote function call can now be made:
And received:
Etc.
And, the more I think about it, your solution could have worked in my situation.
In retrospect, however, I think that having a single parameter of type boost::tuple and using boost::tuple serialization would have been a better bet, and saved the need for this specific code in the first place.