Motivation
The C++ function and template parameter lists are special syntactic constructs, and it is impossible to directly
manipulate or generate them using C++ constructs.
This leads to unnecessary code repetition.
Consider the implementation of the is_function<>
metafunction is Boost.
The implementation uses an overloaded is_function_tester()
function that is used for testing if a type is convertible
to a pointer to a function.
Because of the special treatment of parameter lists, it is not possible to directly match a function with an arbitrary parameter list.
Instead, the is_function_tester()
must be overloaded for every distinct number of parameters that is to be supported.
For example:
template<class R>
yes_type is_function_tester(R (*)());
template<class R, class A0>
yes_type is_function_tester(R (*)(A0));
template<class R, class A0, class A1>
yes_type is_function_tester(R (*)(A0, A1));
template<class R, class A0, class A1, class A2>
yes_type is_function_tester(R (*)(A0, A1, A2));
// ...
The need for this kind of repetition occurs particularly frequently while implementing generic components or metaprogramming facilities,
but the need also manifests itself in many far simpler situations.
Typical Solutions
Typically the repetition is done manually.
Manual code repetition is highly unproductive, but sometimes more readable to the untrained eye.
Another solution is to write an external program for generating the repeated code or use some other extra linguistic means such as a smart editor.
Unfortunately, using external code generators has many disadvantages:
- Writing the generator takes time. (This could be helped by using a standard generator.)
- It is no longer productive to manipulate C++ code directly.
- Invoking the generator may be difficult.
- Automating the invocation of the generator can be difficult in certain environments. (Automatic invocation is desirable for active libraries.)
- Porting and distributing the generator may be difficult or simply takes precious time.
What about the preprocessor?
Because C++ comes with a preprocessor, one would assume that it would support these kinds of needs directly.
Using the preprocessor in this case is highly desirable because:
- The preprocessor is highly portable.
- The preprocessor is automatically invoked as part of the compilation process.
- Preprocessor metacode can be directly embedded into the C++ source code.
- Compilers generally allow viewing or outputting the preprocessed code, which can be used for debugging or to copy and paste the generated code.
Most unfortunately, the preprocessor is a very low level preprocessor that specifically does not support repetition or recursive macros.
Library support is needed!
For detailed information on the capabilities and limitations of the preprocessor, please refer to the C++ standard [Std].
The Motivation Example Revisited
Using the primitives of the preprocessor library, the is_function_tester()
's could be implemented like this:
#include <boost/preprocessor/arithmetic/inc.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition.hpp>
#ifndef MAX_IS_FUNCTION_TESTER_PARAMS
#define MAX_IS_FUNCTION_TESTER_PARAMS 15
#endif
#define IS_FUNCTION_TESTER(Z, N, _) \
template<class R BOOST_PP_COMMA_IF(N) BOOST_PP_ENUM_PARAMS(N, class A)> \
yes_type is_function_tester(R (*)(BOOST_PP_ENUM_PARAMS(N, A))); \
/**/
BOOST_PP_REPEAT(BOOST_PP_INC(MAX_IS_FUNCTION_TESTER_PARAMS), IS_FUNCTION_TESTER, _)
#undef IS_FUNCTION_TESTER
In order to change the maximum number of function parameters supported, you now simply change the MAX_IS_FUNCTION_TESTER_PARAMS
definition and recompile.
Permission to copy, use, modify, sell and distribute this document is granted provided this copyright notice appears in all copies.
This document is provided "as is" without express or implied warranty and with no claim as to its suitability for any purpose.