Click here to Skip to main content
15,894,825 members
Articles / Programming Languages / C++

Wave: a Standard conformant C++ preprocessor library

Rate me:
Please Sign up or sign in to vote.
4.96/5 (58 votes)
10 Jan 200413 min read 401.8K   4.4K   81  
Describes a free and fully Standard conformant C++ preprocessor library
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Preliminary C++0x support</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<link href="theme/style.css" rel="stylesheet" type="text/css">
</head>

<body>
<table width="100%" border="0" cellspacing="2" background="theme/bkd2.gif">
  <tr> 
    <td width="21"> <h1></h1></td>
    <td width="885"> <font face="Verdana, Arial, Helvetica, sans-serif"><b><font size="6">Preliminary 
      C++0x support</font></b></font></td>
    <td width="96"><a href="http://spirit.sf.net"><img src="theme/wave.gif" width="93" height="68" align="right" border="0"></a></td>
  </tr>
</table>
<br>
<table border="0">
  <tr> 
    <td width="10"></td>
    <td width="30"><a href="index.html"><img src="theme/u_arr.gif" border="0"></a></td>
    <td width="30"><a href="class_reference_inputpolicy.html"><img src="theme/l_arr.gif" width="20" height="19" border="0"></a></td>
    <td width="30"><a href="wave_driver.html"><img src="theme/r_arr.gif" border="0"></a></td>
  </tr>
</table>
<p>In order to prepare a proposal for the C++ Standards committee, which should 
  describe certain new and enhanced preprocessor features, the Wave preprocessor 
  library has implemented experimental support for the following features:</p>
<p><a href="preliminary_cpp0x_support.html#variadic_macros">Variadic macros</a><br>
  <a href="preliminary_cpp0x_support.html#placemarker_tokens">Placemarker tokens</a><br>
  <a href="preliminary_cpp0x_support.html#token_pasting">Well defined token-pasting</a><br>
  <a href="preliminary_cpp0x_support.html#macro_scoping">Macro scoping mechanism</a><br>
  <a href="preliminary_cpp0x_support.html#new_alternative_tokens">New alternative tokens</a></p>
<p>The described features are enabled by the <tt>--c++0x</tt> command line option 
  of the <tt>Wave</tt> driver. Alternatively you can enable these features by 
  calling the <tt>wave::context&lt;&gt;::set_language()</tt> function with the 
  <tt>wave::support_cpp0x</tt> value. </p>
<h2><a name="variadic_macros"></a>Variadic macros</h2>
<p>Both variadic macros and placemarker tokens have already been added to C99 
  <a href="references.html#iso_c">[2]</a>. This represents an unnecessary incompatibility 
  between C and C++. Adding these facilities to the C++ preprocessor would cause 
  no code to break that is currently well-defined and would closing the gap between 
  C and C++ in this field.</p>
<p>Variadic macros were added to the C preprocessor as of C99 <a href="references.html#iso_c">[2]</a>. 
  They are, effectively, a way to pass a variable number of arguments to a macro. 
  The specific syntax is as follows:</p>
<pre>    <span class="preprocessor">#define</span> A(...) <span class="preprocessor">__VA_ARGS__</span>
    <span class="preprocessor">#define</span> B(a, ...) <span class="preprocessor">__VA_ARGS__</span>
 
    A(1, 2, 3)     <span class="comment">// expands to: 1, 2, 3</span>
    B(1, 2, 3)     <span class="comment">// expands to: 2, 3</span>
</pre>
<p>The ellipsis is used to denote that the macro can accept any number of trailing 
  arguments. It must always occur as the last formal parameter of the macro. The 
  variadic arguments passed to the macro are identified by the special symbol 
  <tt>__VA_ARGS__</tt> in the replacement list of a variadic macro. The use of 
  this symbol is prohibited in any other context.</p>
<h2><a name="placemarker_tokens"></a>Placemarker tokens</h2>
<p>Placemarker tokens (technically, preprocessing tokens) are simply a well-defined 
  way of passing &quot;nothing&quot; as a macro argument. This facility was also 
  added to the C preprocessor as of C99 <a href="references.html#iso_c">[2]</a>.</p>
<pre>    <span class="preprocessor">#define</span> X(p) f(p)
    X(&quot;abc&quot;)      <span class="comment">// expands to: f(&quot;abc&quot;)</span>
    X()           <span class="comment">// expands to: f()</span>
 
    <span class="preprocessor">#define</span> Y(a, b) <span class="keyword">int</span>[a][b]
    Y(2, 2)       <span class="comment">// expands to: int[2][2]</span>
    Y(, 2)        <span class="comment">// expands to: int[][2]</span>
</pre>
<p>Placemarker tokens are a natural counterpart to variadic macros.  They formalize the optional nature of a variadic argument (or arguments) so that variadic macros appear similar to the variadic functions, but have been generalized to include named parameters as well.
</p>
<h2><a name="token_pasting"></a>Well defined token-pasting</h2>
<p>Currently, as of both C++98 and C99, if token-pasting results in multiple preprocessing 
  tokens, the behavior is undefined. For example,</p>
<pre>    <span class="preprocessor">#define</span> PASTE(a, b) a <span class="preprocessor">##</span> b
    PASTE(1, 2)   <span class="comment">// okay</span>
    PASTE(+, -)   <span class="comment">// undefined behavior</span></pre>
<p>Token-pasting of unrelated tokens (i.e. token-pasting resulting in multiple 
  preprocessing tokens) is currently undefined for no substantial reason. It is 
  not dependent on architecture nor is it difficult for an implementation to diagnose. 
  Furthermore, retokenization is what most, if not all, preprocessors already 
  do and what most programmers already expect the preprocessor to do. Well-defined 
  behavior is simply standardizing existing practice and removing an arbitrary 
  and unnecessary undefined behavior from the standard.</p>
<p>To achieve well-defined behavior in this context <tt>Wave</tt> retokenizes 
  the result of the token-pasting and inserts the newly created token sequence 
  as the macro replacement text.</p>
<pre>    PASTE(+, ==)  <span class="comment">// expands to: += =</span></pre>
<h2><a name="macro_scoping"></a>Macro scoping mechanism</h2>
<p>One of the major problems of the preprocessor is that macro definitions do 
  not respect any of the scoping mechanisms of the core language. As history has 
  shown, this is a major inconvenience and drastically increases the likelihood 
  of name clashes within a translation unit. The solution is to add both a named 
  and unnamed scoping mechanism to the C++ preprocessor. This limits the scope 
  of macro definitions without limiting its accessibility. Here are the details. 
</p>
<p>The scoping mechanism is implemented with the help of three new preprocessor 
  directives: <span class="preprocessor">#region</span>, <span class="preprocessor">#endregion</span> 
  and <span class="preprocessor">#import</span>. Additionally it changes minor 
  details of some of the existing preprocessor directives: <span class="preprocessor">#ifdef</span>, 
  <span class="preprocessor">#ifndef</span> and the <span class="preprocessor">operator 
  defined()</span>.</p>
<h3>The #region directive</h3>
<p>The <span class="preprocessor">#region</span> directive starts a new named 
  or unnamed macro scope.</p>
<h4>Syntax</h4>
<blockquote> 
  <p><tt>#region &lt;qualified-identifier&gt;</tt></p>
  <p> Where <tt>&lt;qualified-identifier&gt;</tt> is an optionally qualified name 
    defining the name of the region to open.<br>
    This name is optional. If the name is omitted a nameless region is opened. 
  </p>
</blockquote>
<p>If the qualified identifier starts with an <tt>'::'</tt> the name is looked 
  up relative to the global scope (the <tt>&lt;qualified-identifier&gt;</tt> is 
  called <em>absolute</em>), if it starts with an identifier, the region is looked 
  up relative to the current open region (the <tt>&lt;qualified-identifier&gt;</tt> 
  is called <em>relative</em>). If the specified region is not defined, it is 
  created. </p>
<p>The <span class="preprocessor">#region</span> directive is opaque for all macro 
  definitions made outside this region, i.e. no macros defined inside of other 
  regions or at the global scope are directly accessible from inside the opened 
  region. To access such macros these must be imported (see the <span class="preprocessor">#import</span> 
  directive) or must be referred to through it's qualified name. </p>
<p>Regions may be nested. </p>
<p>A region may be re-opened (i.e. a <span class="preprocessor">#region</span> 
  directive with the same name is found at the same scope), and macros defined 
  inside the previous occurences of this region will be visible. </p>
<p>Region names and macro names of the same scope are stored into the same symbol 
  table. This implies, that at one scope there shall not be defined a region and 
  a macro with the same name.</p>
<p>Macros defined inside a nameless region may not be accessed from outside this 
  region. Further, from inside a nameless region it is not allowed to open an 
  enclosed region through an <em>absolute</em> name.</p>
<p>The argument of the <span class="preprocessor">#region</span> directive is 
  <strong>not</strong> subject to macro expansion before it is evaluated.</p>
<p>The following is a small code sample, which shows possible usages of preprocessor 
  regions.</p>
<pre>    <span class="preprocessor">#define</span> A() 1
    
    <span class="comment">/////////////////////////////////////</span>
    <span class="preprocessor">#region</span> region_A
    <span class="preprocessor"># define</span> B() 2
 
    <span class="comment">/////////////////////////////////////</span>
    <span class="preprocessor"># region</span> region_B
    <span class="preprocessor">#  define</span> C() 3
    A() <span class="comment">// expands to: A()</span>
    B() <span class="comment">// expands to: B()</span>
    C() <span class="comment">// expands to: 3</span>
    <span class="preprocessor"># endregion</span> <span class="comment">// region_B</span>
    <span class="comment">/////////////////////////////////////</span>
 
    A() <span class="comment">// expands to: A()</span>
    B() <span class="comment">// expands to: 2</span>
    C() <span class="comment">// expands to: C()</span>
    region_B::C()             <span class="comment">// expands to: 3</span>
    ::region_A::region_B::C() <span class="comment">// expands to: 3</span>
    <span class="preprocessor">#endregion</span> <span class="comment">// region_A</span>
    <span class="comment">/////////////////////////////////////</span>

    A() <span class="comment">// expands to: 1</span>
    B() <span class="comment">// expands to: B()</span>
    region_A::B()   <span class="comment">// expands to: 2</span>
    ::region_A::B() <span class="comment">// expands to: 2</span>
    region_A::region_B::C()   <span class="comment">// expands to: 3</span>
    ::region_A::region_B::C() <span class="comment">// expands to: 3</span>
 
    <span class="preprocessor">#define</span> region_A ... <span class="comment">// error, name clash with region_A</span>
    <span class="preprocessor">#region</span> A            <span class="comment">// error, name clash with macro A</span>
    <span class="preprocessor">#endregion</span>
</pre>
<h3>The #endregion directive</h3>
<p>The <span class="preprocessor">#endregion</span> directive closes the last 
  macro scope opened with a <span class="preprocessor">#region</span> directive 
  .</p>
<h4>Syntax</h4>
<blockquote> 
  <p><tt>#endregion</tt></p>
</blockquote>
<p>The <span class="preprocessor">#endregion</span> directive is opaque for all 
  macros defined inside the closed region. Macros defined inside this region may 
  be accessed from outside of this region only if imported (see the <span class="preprocessor"> 
  #import</span> directive) or if referenced through qualified names specifying 
  the region and the macro name and if the region isn't unnamed.</p>
<p>The <span class="preprocessor">#region</span> and <span class="preprocessor">#endregion</span> 
  directives shall be balanced over the whole translation unit. Otherwise an error 
  is raised.</p>
<h3>The #import directive</h3>
<p>The <span class="preprocessor">#import</span> directive allows to import macros 
  or whole macro scopes into the current macro scope.</p>
<h4>Syntax</h4>
<blockquote> 
  <p><tt>#import &lt;qualified-identifier&gt; [, &lt;qualified-identifier&gt; 
    ...]</tt></p>
</blockquote>
<blockquote>
  <p>Where <tt>&lt;qualified-identifier&gt;</tt> is an optionally qualified name 
    defining the name of the macro or region to import. The <span class="preprocessor">#import</span> 
    directive may specify one or more comma separated qualified names.</p>
</blockquote>
<p>If the qualified identifier starts with an <tt>'::'</tt> the name is looked 
  up relative to the global scope (the <tt>&lt;qualified-identifier&gt;</tt> is 
  called <em>absolute</em>), if it starts with an identifier, the region is looked 
  up relative to the current open region (the <tt>&lt;qualified-identifier&gt;</tt> 
  is called <em>relative</em>). </p>
<p>If <tt>&lt;qualified-identifier&gt;</tt> refers to a macro, then the referenced 
  macro definition is made available in the current region, just if it were defined 
  here. Both macro definitions (the original macro definition and the imported 
  one) refer to the same macro. This is significant for disabling of a certain 
  macro during the rescanning of a replacement list. If one of the different instances 
  of the macro definition is marked as disabled for expansion, the others are 
  marked as disabled for expansion too.</p>
<p>If <tt>&lt;qualified-identifier&gt;</tt> refers to a region, then</p>
<ul>
  <li>all macro definitions of the referenced region are made available in the 
    current region, just if these were defined here.</li>
  <li> all sub-regions of the referenced region are made available from the current 
    region, just if these were defined as direct sub-regions of the current region.</li>
</ul>
<p> Imported macros may be undefined with the <span class="preprocessor">#undef</span> 
  directive as usual. This removes the referenced macro from the current region, 
  but leaves it unchanged in the original region, where it was defined initially.<br>
  <br>
  The argument of the <span class="preprocessor">#import</span> directive is <strong>not</strong> 
  subject to macro expansion before it is evaluated. </p>
<h3>Changes to the #ifdef, #ifndef directives and the operator defined()</h3>
<p>To fully support macro regions, the <span class="preprocessor">#ifdef</span> 
  and <span class="preprocessor">#ifndef</span> directives and the <span class="preprocessor">operator 
  defined()</span> may be used with qualified identifiers as its arguments too. 
  Therefor the following sample is completely wellformed (courtesy to Paul Mensonides):</p>
<pre>    <span class="preprocessor"># ifndef</span> ::CHAOS_PREPROCESSOR::chaos::WSTRINGIZE_HPP
    <span class="preprocessor"># region</span> ::CHAOS_PREPROCESSOR::chaos
    <span class="preprocessor">#</span>
    <span class="preprocessor"># define</span> WSTRINGIZE_HPP
    <span class="preprocessor">#</span>
    <span class="preprocessor"># include</span> <span class="copyright">&lt;chaos/experimental/cat.hpp&gt;</span>
    <span class="preprocessor">#</span>
    <span class="preprocessor">#</span> <span class="comment">// wstringize</span>
    <span class="preprocessor">#</span>
    <span class="preprocessor"># define</span> wstringize(...) \
        chaos::primitive_wstringize(__VA_ARGS__) \
        <span class="comment">/**/</span>
    <span class="preprocessor">#</span>
    <span class="preprocessor">#</span> <span class="comment">// primitive_wstringize</span>
    <span class="preprocessor">#</span>
    <span class="preprocessor"># define</span> primitive_wstringize(...) \
        chaos::primitive_cat(L, #__VA_ARGS__) \
        <span class="comment">/**/</span>
    <span class="preprocessor">#</span>
    <span class="preprocessor"># endregion</span>
    <span class="preprocessor"># endif</span>

    <span class="preprocessor"># import</span> ::CHAOS_PREPROCESSOR
 
    chaos::wstringize(a,b,c) <span class="comment">// L&quot;a,b,c&quot;</span>
</pre>
<p>In the context of the <span class="preprocessor">#ifdef</span> and <span class="preprocessor">#ifndef</span> 
  directives and the <span class="preprocessor">operator defined()</span> a qualified 
  macro name is considered to be defined if:</p>
<ul>
  <li>all regions referenced by the qualified name exist (opened at least once) 
    and</li>
  <li>the referenced macro scope contains the definition of a macro with the given 
    name.</li>
</ul>
<h2><a name="new_alternative_tokens"></a>New alternative tokens</h2>
<p>Vesa Karvonen recently suggested on the <a href="http://aspn.activestate.com/ASPN/Mail/Message/boost/1618972">Boost 
  mailing list</a> the following addition to the preprocessor, which is implemented 
  by <tt>Wave</tt> in C++0x mode.</p>
<p>Consider the following example:</p>
<pre>    <span class="preprocessor">#define</span> ID(x) x
    ID( (         )
    ID(   a , b   )
    ID(         ) )</pre>
<p>The macro expansion of the above preprocessor code does not produce the intended 
  result: </p>
<pre>    ( a , b )</pre>
<p>The basic idea is that the keywords <tt>__lparen__</tt>, <tt>__rparen__</tt> 
  and <tt>__comma__</tt> could be used in place of <span class="literal">'('</span>, 
  <span class="literal">')'</span> and <span class="literal">','</span>, respectively. 
  The <br>
  above example would now become:</p>
<pre>    <span class="preprocessor">#define</span> ID(x) x
    ID( __lparen__                          )
    ID(            a __comma__ b            )
    ID(                          __rparen__ )</pre>
<p>and it would expand into:</p>
<pre>    __lparen__ a __comma__ b __rparen__</pre>
<p>which would be recognized in translation phases after macro replacement as 
  equivalent to the token sequence:</p>
<pre>    ( a , b )</pre>
<p>This trivial extension makes it an order of magnitude easier to generate C++ 
  code using the C++ preprocessor.</p>
<table border="0">
  <tr> 
    <td width="10"></td>
    <td width="30"><a href="index.html"><img src="theme/u_arr.gif" border="0"></a></td>
    <td width="30"><a href="class_reference_inputpolicy.html"><img src="theme/l_arr.gif" width="20" height="19" border="0"></a></td>
    <td width="30"><a href="wave_driver.html"><img src="theme/r_arr.gif" border="0"></a></td>
  </tr>
</table>
<hr size="1">
<p class="copyright">Copyright &copy; 2003-2004 Hartmut Kaiser<br>
  Copyright &copy; 2003 Paul Mensonides<br>
  Copyright &copy; 2003 Vesa Karvonen<br>
  <br>
  <font size="2">Use, modification and distribution is subject to the Boost Software 
  License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 
  </font> </p>
<span class="updated">Last updated: 
<!-- #BeginDate format:fcAm1m -->Monday, January 5, 2004  14:57<!-- #EndDate -->
</span>
<p>&nbsp;</p>
</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
Actively involved in Boost and the development of the Spirit parser construction framework.

Comments and Discussions