mirror of
https://github.com/boostorg/preprocessor.git
synced 2025-07-01 06:50:59 +02:00
479 lines
17 KiB
HTML
479 lines
17 KiB
HTML
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
|
|
<link rel="stylesheet" type="text/css" href="../../../boost.css">
|
|
<title>Boost.Preprocessor - Tutorial</title>
|
|
</head>
|
|
<body link="#0000ff" vlink="#800080">
|
|
<table border="0" cellpadding="7" cellspacing="0" width="100%" summary=
|
|
"header">
|
|
<tr>
|
|
<td valign="top" width="300">
|
|
<h3><a href="../../../index.htm"><img height="86" width="277" alt="C++ Boost" src="../../../c++boost.gif" border="0"></a></h3>
|
|
</td>
|
|
<td valign="top">
|
|
<h1 align="center">Boost.Preprocessor</h1>
|
|
<h2 align="center">Tutorial</h2>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<hr>
|
|
|
|
<h2>Contents</h2>
|
|
<dl class="page-index">
|
|
<dt><a href="#Motivation">Motivation</a></dt>
|
|
<dt><a href="#Techniques">Preprocessor Metaprogramming Techniques</a>
|
|
<dl class="page-index">
|
|
<dt><a href="#Local Macro">Use a Local Macro to avoid small scale repetition</a></dt>
|
|
<dt><a href="#UNUSED">Use BOOST_PP_EMPTY as an unused parameter in Local
|
|
Macro instantiations</a></dt>
|
|
<dt><a href="#CAT">Use BOOST_PP_CAT instead of ## when necessary</a></dt>
|
|
<dt><a href="#STRINGIZE">Use BOOST_PP_STRINGIZE instead of # whenever necessary</a></dt>
|
|
<dt><a href="#ENUM_PARAMS">Avoid O(N) repetition on lists in general</a></dt>
|
|
<dt><a href="#Conditional Define">Use a Conditional Define to enable user configuration of code repetition</a></dt>
|
|
<dt><a href="#Token Look-Up">Use Token Look-Up Function to eliminate categorical repetition</a></dt>
|
|
<dt><a href="#2ND_REPEAT">Use BOOST_PP_REPEAT to avoid O(N*N) repetition</a></dt>
|
|
<dt><a href="#IF">Use BOOST_PP_IF to implement special case for the first element</a></dt>
|
|
<dt><a href="#Arithmetic">Use arithmetic, logical and comparison operations when necessary</a></dt>
|
|
</dl>
|
|
</dt>
|
|
</dl>
|
|
|
|
<hr>
|
|
|
|
<h2><a name="Motivation">Motivation</a></h2>
|
|
|
|
<p>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.</p>
|
|
<p> Consider the implementation of the is_function<> metafunction in Boost. The
|
|
implementation uses an overloaded is_function_tester() function that is used
|
|
for testing if a type is convertible to 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.
|
|
Example:</p>
|
|
|
|
<pre>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, A1>
|
|
yes_type is_function_tester(R (*)(A0, A1));
|
|
template <class R, class A0, A1, A2>
|
|
yes_type is_function_tester(R (*)(A0, A1, A2));
|
|
|
|
// ...
|
|
</pre>
|
|
|
|
<P>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. </P>
|
|
|
|
<h3>Typical solutions</h3>
|
|
|
|
<p>Typically the repetition is done manually. Manual code repetition is highly
|
|
unproductive, but sometimes more readable to the untrained eye.</p>
|
|
<p>Another solution is to write an external program for generating the repeated
|
|
code or use some other extra linquistic means such as a smart editor. Unfortunately,
|
|
using external code generators has many disadvantages:</p>
|
|
<ul>
|
|
<li>writing the generator takes time (this could be helped by using a standard
|
|
generator)</li>
|
|
<li>it is no longer productive to manipulate C++ code directly</li>
|
|
<li>invoking the generator may be difficult</li>
|
|
<li>automating the invocation of the generator can be difficult in certain environments
|
|
(automatic invocation is desirable for active libraries)</li>
|
|
<li>porting and distributing the generator may be difficult or simply takes
|
|
precious time</li>
|
|
</ul>
|
|
<h3>What about the preprocessor?</h3>
|
|
<p>Because C++ comes with a preprocessor, one would assume that it would support
|
|
these kind of needs directly. Using the preprocessor in this case is highly
|
|
desirable because:</p>
|
|
<ul>
|
|
<li>preprocessor is highly portable</li>
|
|
<li>preprocessor is automatically invoked as part of the compiling process</li>
|
|
<li>preprocessor metacode can be directly embedded into the C++ source code</li>
|
|
<li>Compilers generally allow to view or output the preprocessed code, which
|
|
can be used for debugging or to copy-paste the generated code.</li>
|
|
</ul>
|
|
<p>Most unfortunately, the preprocessor is a very low level preprocessor that
|
|
specifically does not support repetition or recursive macros. Library support
|
|
is needed!</p>
|
|
<p><i>For detailed information on the capabilities and limitations of the preprocessor,
|
|
please refer to the C++ standard <A href="bibliography.htm#[Std]">[Std]</A>.</i></p>
|
|
<h3>The motivation example revisited</h3>
|
|
<p>Using the primitives of the PREPROCESSOR library, the is_function_tester()s
|
|
could be implemented like this:</p>
|
|
|
|
<pre>#define IS_FUNCTION_TESTER(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
|
|
</pre>
|
|
|
|
<p>In order to change the maximum number of function parameters supported, you
|
|
now simply change the MAX_IS_FUNCTION_TESTER_PARAMS definition and recompile.</p>
|
|
<HR>
|
|
|
|
<H2><a name="Techniques">Preprocessor Metaprogramming Techniques</a></H2>
|
|
<P>The preprocessor metaprogramming techniques are presented in example format.
|
|
</P>
|
|
<HR>
|
|
<P><B><a name="Local Macro"></a><a href="examples_preprocessed.htm#Local Macro">EXAMPLE</a>:</B>
|
|
Use a Local Macro to avoid small scale repetition</P>
|
|
|
|
<pre>#define BOOST_PP_DEF(OP) \
|
|
template<class T, int n> \
|
|
vec<T,n>& \
|
|
operator OP##= \
|
|
( vec<T,n>& \
|
|
lhs \
|
|
, const vec<T,n>& \
|
|
rhs \
|
|
) \
|
|
{ for (int i=0; i<n; ++i)\
|
|
lhs(i) OP##= rhs(i); \
|
|
return lhs; \
|
|
}
|
|
|
|
BOOST_PP_DEF(+)
|
|
BOOST_PP_DEF(-)
|
|
BOOST_PP_DEF(*)
|
|
BOOST_PP_DEF(/)
|
|
#undef BOOST_PP_DEF
|
|
</pre>
|
|
|
|
<P><B>TIP:</B> It is usually okay to use a standard macro name like BOOST_PP_DEF
|
|
for this kind of code, because the macro is both defined and undefined in the
|
|
immediate site of its use.</P>
|
|
<P><B>TIP:</B> It is easier to verify proper use of
|
|
the line continuation operator when they are aligned.</P>
|
|
<P><B>NOTES:</B> You can extend this example by defining more and different kinds
|
|
of operators. Before doing so, consider using the Algebraic Categories technique
|
|
introduced in <A href="bibliography.htm#[Barton]">[Barton]</A> or a Layered Architecture (see for instance
|
|
<A href="bibliography.htm#[Czarnecki]">[Czarnecki]</A>). However, at some point you must type the operator tokens
|
|
*, /, +, -, ..., because it is impossible to generate them using templates.
|
|
The resulting Categorical Repetition of tokens can be eliminated by using preprocessor
|
|
metaprogramming.</P>
|
|
<HR>
|
|
<P><B><a name="UNUSED"></a><a href="examples_preprocessed.htm#UNUSED">EXAMPLE</a>:</B>
|
|
Use BOOST_PP_EMPTY as an unused parameter in Local Macro instantiations</P>
|
|
|
|
<pre>#define BOOST_PP_DEF(CV) \
|
|
template<class base> \
|
|
CV() typename implement_subscript_using_begin_subscript<base>::value_type&\
|
|
implement_subscript_using_begin_subscript<base>::operator[]\
|
|
( index_type \
|
|
i \
|
|
) CV() \
|
|
{ return base::begin()[i];\
|
|
}
|
|
|
|
BOOST_PP_DEF(BOOST_PP_EMPTY)
|
|
BOOST_PP_DEF(const BOOST_PP_EMPTY)
|
|
#undef BOOST_PP_DEF
|
|
</pre>
|
|
|
|
<P><B>HOW:</B> BOOST_PP_EMPTY() expands to nothing and can be used as
|
|
an unused parameter.</P>
|
|
<P><b>NOTE:</b> BOOST_PP_EMPTY without the () never gets expanded. The
|
|
() is necessary to invoke a function-like macro.</P>
|
|
<B>CAVEAT:</B> You can not safely use concatenation while using BOOST_PP_EMPTY().
|
|
<P><B>TIP:</B> Occasionally one or two lines are
|
|
considerably longer than the rest. It can often save some work to not align all
|
|
of the line continuation operators without making the code too unreadable.</P>
|
|
<P><B>TIP:</B> Use syntax highlighting on preprocessor metaprogramming macro and
|
|
parameter identifiers such as</P>
|
|
<ul>
|
|
<li>BOOST_PP_DEF,</li>
|
|
<li>BOOST_PP_EMPTY,</li>
|
|
<li>BOOST_PP_REPEAT,</li>
|
|
<li>OP,</li>
|
|
<li>CV,</li>
|
|
<li>...</li>
|
|
</ul>
|
|
<p>It can greatly improve readability.</p>
|
|
<HR>
|
|
<P><a name="CAT"></a><a href="examples_preprocessed.htm#CAT"><B>EXAMPLE:</B></a> Use BOOST_PP_CAT instead of ## when necessary</P>
|
|
|
|
<pre>#define STATIC_ASSERT(EXPR)\
|
|
enum\
|
|
{ BOOST_PP_CAT(static_check_,__LINE__) = (EXPR) ? 1 : -1\
|
|
};\
|
|
typedef char\
|
|
BOOST_PP_CAT(static_assert_,__LINE__)\
|
|
[ BOOST_PP_CAT(static_check_,__LINE__)\
|
|
]
|
|
|
|
// ...
|
|
|
|
STATIC_ASSERT(sizeof(int) <= sizeof(long));
|
|
</pre>
|
|
|
|
<P><B>WHY:</B> Macro expansion proceeds recursively in "layers". Token pasting
|
|
prevents the preprocessor from performing macro expansion, therefore it
|
|
is often necessary to delay token concatenation.</P>
|
|
<hr>
|
|
<p><a name="STRINGIZE"></a><a href="examples_preprocessed.htm#STRINGIZE"><B>EXAMPLE:</B></a> Use BOOST_PP_STRINGIZE instead of # whenever necessary</p>
|
|
|
|
<pre>#define NOTE(STR)\
|
|
message(__FILE__ "(" BOOST_PP_STRINGIZE(__LINE__) ") : " STR)
|
|
|
|
// ...
|
|
|
|
#pragma NOTE("TBD!")
|
|
</pre>
|
|
|
|
<p><b>WHY:</b> Macro expansion proceeds recursively in "layers". Stringization
|
|
prevents the preprocessor from performing macro expansion, therefore it is often
|
|
necessary to delay stringization.</p>
|
|
<HR>
|
|
<P><B><a name="ENUM_PARAMS"></a><a href="examples_preprocessed.htm#ENUM_PARAMS">EXAMPLE</a>:</B>
|
|
Use:</P>
|
|
<ul>
|
|
<li>BOOST_PP_ENUM_PARAMS,</li>
|
|
<li>BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT,</li>
|
|
<li>BOOST_PP_ENUM_PARAMS_WITH_DEFAULTS,</li>
|
|
<li>BOOST_PP_ENUM_SHIFTED_PARAMS, or</li>
|
|
<li>BOOST_PP_REPEAT, and</li>
|
|
<li>BOOST_PP_COMMA_IF</li>
|
|
</ul>
|
|
<p>to avoid O(N) repetition on lists in general</p>
|
|
|
|
<pre>struct make_type_list_end;
|
|
|
|
template
|
|
< BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT
|
|
( MAKE_TYPE_LIST_MAX_LENGTH
|
|
, class T
|
|
, make_type_list_end
|
|
)
|
|
>
|
|
struct make_type_list
|
|
{
|
|
private:
|
|
enum
|
|
{ end = is_same<T0,make_type_list_end>::value
|
|
};
|
|
|
|
public:
|
|
typedef typename
|
|
type_if
|
|
< end
|
|
, type_cons_empty
|
|
, type_cons
|
|
< T0
|
|
, typename
|
|
type_inner_if
|
|
< end
|
|
, type_identity<end>
|
|
, make_type_list
|
|
< BOOST_PP_ENUM_SHIFTED_PARAMS
|
|
( MAKE_TYPE_LIST_MAX_LENGTH
|
|
, T
|
|
)
|
|
>
|
|
>::type
|
|
>
|
|
>::type type;
|
|
};
|
|
</pre>
|
|
|
|
<P><B>HOW:</B> BOOST_PP_REPEAT uses simulated recursion (pseudo code):</P>
|
|
|
|
<pre>#define BOOST_PP_REPEAT(N,M,P) BOOST_PP_REPEAT##N(M,P)
|
|
#define BOOST_PP_REPEAT0(M,P)
|
|
#define BOOST_PP_REPEAT1(M,P) M(0,P)
|
|
#define BOOST_PP_REPEAT2(M,P) M(0,P) M(1,P)
|
|
#define BOOST_PP_REPEAT3(M,P) BOOST_PP_REPEAT2(M,P) M(2,P)
|
|
#define BOOST_PP_REPEAT4(M,P) BOOST_PP_REPEAT3(M,P) M(3,P)
|
|
// ...
|
|
</pre>
|
|
|
|
<P>BOOST_PP_ENUM_PARAMS variations use BOOST_PP_REPEAT</P>
|
|
|
|
<P>BOOST_PP_COMMA_IF(I) expands to a comma if I != 0.</P>
|
|
|
|
<P>BOOST_PP_INC(I) expands essentially to "I+1" and BOOST_PP_DEC(I)
|
|
expands essentially to "I-1".</P>
|
|
|
|
<HR>
|
|
|
|
<P><a name="Conditional Define"><B>EXAMPLE:</B></a> Use a Conditional Define to
|
|
enable user configuration of code repetition based on need rather than some
|
|
"reasonable" upper limit</P>
|
|
|
|
<pre>#ifndef MAKE_TYPE_LIST_MAX_LENGTH
|
|
#define MAKE_TYPE_LIST_MAX_LENGTH 8
|
|
#endif
|
|
</pre>
|
|
|
|
<P>Now the user can configure the make_type_list primitive without modifying library
|
|
code.</P>
|
|
<HR>
|
|
<P><B><a name="Token Look-Up"></a><a href="examples_preprocessed.htm#Token Look-Up">EXAMPLE</a>:</B>
|
|
Use BOOST_PP_REPEAT and a Token Look-Up Function to eliminate categorical
|
|
repetition</P>
|
|
|
|
<pre>// CAVEAT: My compiler is not standard on arithmetic types.
|
|
#define ARITHMETIC_TYPE(I) ARITHMETIC_TYPE##I
|
|
#define ARITHMETIC_TYPE0 bool
|
|
#define ARITHMETIC_TYPE1 char
|
|
#define ARITHMETIC_TYPE2 signed char
|
|
#define ARITHMETIC_TYPE3 unsigned char
|
|
#define ARITHMETIC_TYPE4 short
|
|
#define ARITHMETIC_TYPE5 unsigned short
|
|
#define ARITHMETIC_TYPE6 int
|
|
#define ARITHMETIC_TYPE7 unsigned int
|
|
#define ARITHMETIC_TYPE8 long
|
|
#define ARITHMETIC_TYPE9 unsigned long
|
|
#define ARITHMETIC_TYPE10 float
|
|
#define ARITHMETIC_TYPE11 double
|
|
#define ARITHMETIC_TYPE12 long double
|
|
#define ARITHMETIC_TYPE_CNT 13
|
|
|
|
// ...
|
|
|
|
#define BOOST_PP_DEF(I,_)\
|
|
catch (ARITHMETIC_TYPE(I) t)\
|
|
{ report_typeid(t);\
|
|
report_value(t);\
|
|
}
|
|
BOOST_PP_REPEAT
|
|
( ARITHMETIC_TYPE_CNT
|
|
, BOOST_PP_DEF
|
|
, _
|
|
)
|
|
#undef BOOST_PP_DEF
|
|
|
|
// ...
|
|
</pre>
|
|
|
|
<P><B>NOTE:</B> The repetition of the above
|
|
example can be eliminated using template metaprogramming <A href="bibliography.htm#[Czarnecki]">[Czarnecki]</A> as well. However
|
|
categorical repetition of operator tokens can not be completely eliminated by
|
|
using template metaprogramming.</P>
|
|
<HR>
|
|
<P><B><a name="2ND_REPEAT"></a><a href="examples_preprocessed.htm#2ND_REPEAT">EXAMPLE</a>:</B>
|
|
Use BOOST_PP_REPEAT to avoid O(N*N) repetition</P>
|
|
|
|
<pre>#ifndef MAX_VEC_ARG_CNT
|
|
#define MAX_VEC_ARG_CNT 8
|
|
#endif
|
|
|
|
// ...
|
|
|
|
#define ARG_FUN(I,_) BOOST_PP_COMMA_IF(I) T a##I
|
|
#define ASSIGN_FUN(I,_) (*this)[I] = a##I;
|
|
|
|
#define DEF_VEC_CTOR_FUN(I,_)\
|
|
vec( BOOST_PP_REPEAT(I,ARG_FUN,_) )\
|
|
{ BOOST_PP_REPEAT(I,ASSIGN_FUN,_)\
|
|
}
|
|
|
|
BOOST_PP_REPEAT
|
|
( BOOST_PP_INC(MAX_VEC_ARG_CNT)
|
|
, DEF_VEC_CTOR_FUN
|
|
, _
|
|
)
|
|
|
|
#undef ARG_FUN
|
|
#undef ASSIGN_FUN
|
|
#undef DEF_VEC_CTOR_FUN
|
|
|
|
// ...
|
|
</pre>
|
|
|
|
<P><B>HOW:</B> BOOST_PP_REPEAT is implemented in a special way to enable
|
|
automatic recursion.</P>
|
|
|
|
<HR>
|
|
|
|
<P><a name="IF"></a><a href="examples_preprocessed.htm#IF"><B>EXAMPLE:</B></a> Use BOOST_PP_IF to implement special case for the first element</P>
|
|
|
|
<pre>#define BOOST_PP_COMMA_IF(C)\
|
|
BOOST_PP_IF(C,BOOST_PP_COMMA,BOOST_PP_EMPTY)()
|
|
|
|
BOOST_PP_IF(0,true,false) == false;
|
|
BOOST_PP_IF(1,true,false) == true;
|
|
</pre>
|
|
|
|
<P>BOOST_PP_IF enables convenient generation of lists using BOOST_PP_REPEAT.</P>
|
|
|
|
<P><B>NOTE:</B> THEN and ELSE don't have to be macros. However, if at least one
|
|
of them is a function-like macro and you want it to be expanded conditionally,
|
|
you have to make the other parameter a function-like macro, too. This can often
|
|
be done using BOOST_PP_IDENTITY. Consider the following example (by
|
|
Aleksey Gurtovoy):</P>
|
|
|
|
<pre>#define NUMBERED_EXPRESSION(I,X)\
|
|
BOOST_PP_IF \
|
|
( I \
|
|
, BOOST_PP_IDENTITY(X##I) \
|
|
, BOOST_PP_EMPTY \
|
|
)()</pre>
|
|
|
|
<P><b>NOTE:</b> Like in the above implementation of COMMA_IF, the result of IF
|
|
is often invoked and not the THEN and ELSE parameters. If the parameters were
|
|
invoked, the code would not expand correctly, because the EMPTY parameter would
|
|
get expanded to nothing before the IF would be properly expanded.</P>
|
|
<P><b>HOW:</b> BOOST_PP_IF is defined for the entire repeat range (pseudo
|
|
code):</P>
|
|
|
|
<pre>#define BOOST_PP_IF(C,THEN,ELSE) BOOST_PP_IF##C(THEN,ELSE)
|
|
#define BOOST_PP_IF0(THEN,ELSE) ELSE
|
|
#define BOOST_PP_IF1(THEN,ELSE) THEN
|
|
#define BOOST_PP_IF2(THEN,ELSE) THEN
|
|
// ...
|
|
</pre>
|
|
|
|
<hr>
|
|
|
|
<p><a name="Arithmetic"></a><a href="examples_preprocessed.htm#Arithmetic"><B>EXAMPLE:</B></a> Use arithmetic, logical and comparison operations when necessary</p>
|
|
|
|
<P>The PREPROCESSOR library supports saturated arithmetic, logical and
|
|
comparison operations on decimal integer literals in the range [0,BOOST_PP_LIMIT_MAG].</p>
|
|
|
|
<p>Suppose that you want to generate a numbered lists with a special element inserted
|
|
at a desired position. For example: E0, E1, S, E2. Consider the following example:</p>
|
|
|
|
<pre>#define SPECIAL_NUMBERED_LIST(N,I,ELEM,SPECIAL)\
|
|
BOOST_PP_ASSERT_MSG(BOOST_PP_LESS(I,N),BAD PARAMS FOR SPECIAL_NUMBERED_LIST!)\
|
|
BOOST_PP_ENUM_PARAMS(I,ELEM)\
|
|
BOOST_PP_COMMA_IF(I) SPECIAL\
|
|
BOOST_PP_REPEAT(BOOST_PP_SUB(\
|
|
BOOST_PP_DEC(N),I),SPECIAL_NUMBERED_LIST_HELPER,(ELEM,I))
|
|
#define SPECIAL_NUMBERED_LIST_HELPER(I,ELEM_BASE)\
|
|
,\
|
|
BOOST_PP_CAT\
|
|
( BOOST_PP_TUPLE_ELEM(2,0,ELEM_BASE)\
|
|
, BOOST_PP_ADD\
|
|
( I\
|
|
, BOOST_PP_TUPLE_ELEM(2,1,ELEM_BASE)\
|
|
)\
|
|
)
|
|
|
|
SPECIAL_NUMBERED_LIST(3,0,E,S)
|
|
SPECIAL_NUMBERED_LIST(3,1,E,S)
|
|
SPECIAL_NUMBERED_LIST(3,2,E,S)
|
|
SPECIAL_NUMBERED_LIST(3,3,E,S)
|
|
</pre>
|
|
|
|
<hr>
|
|
<p>Revised
|
|
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
|
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
|
</p>
|
|
<p><i>© Copyright <a href="http://www.housemarque.com">Housemarque Oy</a> 2002</i></p>
|
|
|
|
<p>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.</p>
|
|
</body>
|
|
</html>
|