Click here to Skip to main content
15,861,168 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 392.1K   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="20"><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>
<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 from 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>
<h4>Syntax</h4>
<blockquote> 
  <p><tt>#endregion</tt></p>
</blockquote>
<p>The #endregion directive closes the last opened region. It is opaque for all 
  macros defined inside the closed region. Macros from 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 used as 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>
<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 region to open. The #import 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 <strong>copied</strong> into the current region, just if 
  it were defined here.</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 <strong>copied</strong> 
    into 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<br>
    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 (curtesy 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>
<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="20"><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 Hartmut Kaiser<br>Copyright &copy; 2003 Paul Mensonides<br>Copyright &copy; 2003 Vesa Karvonen<br>
  <br>
  <font size="2">Permission to copy, use, modify, sell and distribute this document 
  is granted provided this copyright notice appears in all copies. This document 
  is provided &quot;as is&quot; without express or implied warranty, and with 
  no claim as to its suitability for any purpose. </font> </p>
<span class="updated">Last updated: 
<!-- #BeginDate format:fcAm1m -->Monday, May 12, 2003  15:44<!-- #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