spirit/classic/phoenix/doc/operators_revisited.html
Joel de Guzman 994d4e48cc moving stuff to classic spirit
[SVN r44163]
2008-04-10 23:51:31 +00:00

201 lines
24 KiB
HTML

<html>
<head>
<!-- Generated by the Spirit (http://spirit.sf.net) QuickDoc -->
<title>Operators revisited</title>
<link rel="stylesheet" href="theme/style.css" type="text/css">
<link rel="prev" href="composites_revisited.html">
<link rel="next" href="interfacing.html">
</head>
<body>
<table width="100%" height="48" border="0" background="theme/bkd2.gif" cellspacing="2">
<tr>
<td width="10">
</td>
<td width="85%">
<font size="6" face="Verdana, Arial, Helvetica, sans-serif"><b>Operators revisited</b></font>
</td>
<td width="112"><a href="http://spirit.sf.net"><img src="theme/spirit.gif" align="right" border="0"></a></td>
</tr>
</table>
<br>
<table border="0">
<tr>
<td width="30"><a href="../index.html"><img src="theme/u_arr.gif" border="0"></a></td>
<td width="30"><a href="composites_revisited.html"><img src="theme/l_arr.gif" border="0"></a></td>
<td width="20"><a href="interfacing.html"><img src="theme/r_arr.gif" border="0"></a></td>
</tr>
</table>
<p>
Each C++ operator has a special tag type associated with it. For example the binary + operator has a plus_op tag type associated with it. This operator tag is used to specialize either:</p>
<ol><li>unary_operator&lt;TagT, T0&gt;</li><li>binary_operator&lt;TagT, T0, T1&gt;</li></ol><p>
template classes (see unary_operator and binary_operator below). Specializations of these unary_operator and binary_operator are the actual workhorses that implement the operations. The behavior of each lazy operator depends on these unary_operator and binary_operator specializations.</p>
<p>
Preset specializations conform to the canonical operator rules modeled by the behavior of integers and pointers:</p>
<ul><li>Prefix -, + and ~ accept constant arguments and return an object by value.</li><li>The ! accept constant arguments and returns a boolean result.</li><li>The &amp; (address-of), * (dereference) both return a reference to an object.</li><li>Prefix ++ returns a reference to its mutable argument after it is incremented.</li><li>Postfix ++ returns the mutable argument by value before it is incremented.</li><li>The += and its family accept mutable right hand side (rhs) operand and return a reference to the rhs operand.</li><li>Infix + and its family accept constant arguments and return an object by value.</li><li>The == and its family accept constant arguments and return a boolean result.</li><li>Operators &amp;&amp; and || accept constant arguments and return a boolean result and are short circuit evaluated as expected.</li></ul><a name="special_operators_and_extensibility"></a><h2>Special operators and extensibility</h2><p>
It is of course possible to override the standard operator behavior when appropriate. For example, the behavior of std::cout does not conform to the canonocal shift left operator &lt;&lt; (i.e. the rhs std::cout is a mutable reference). Odd balls such as this are placed in special_ops.hpp. There you will find specializations for various classes found in the standard lib.</p>
<p>
The library is meant to be extensible. Users may implement their own specializations to allow other libraries to be adapted to be partial-function-evaluation savvy. Later on, in the section &quot;Interfacing (to applications, libraries and frameworks)&quot;, discussion will be focused on interfacing and extending the framework.</p>
<a name="operator_tags"></a><h2>Operator tags</h2><p>
Each C++ operator has a corresponding tag type. This is used as a means for specializing the unary_operator and binary_operator (see below). The tag also serves as the lazy operator type compatible with a composite as an operation (see composite). Here are two examples of operator tags:</p>
<p>
Unary example:</p>
<code><pre>
<span class=keyword>struct </span><span class=identifier>negative_op </span><span class=special>{
</span><span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T0</span><span class=special>&gt;
</span><span class=keyword>struct </span><span class=identifier>result </span><span class=special>{
</span><span class=keyword>typedef </span><span class=keyword>typename </span><span class=identifier>unary_operator</span><span class=special>&lt;</span><span class=identifier>negative_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>&gt;
::</span><span class=identifier>result_type </span><span class=identifier>type</span><span class=special>;
};
</span><span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T0</span><span class=special>&gt;
</span><span class=keyword>typename </span><span class=identifier>unary_operator</span><span class=special>&lt;</span><span class=identifier>negative_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>&gt;::</span><span class=identifier>result_type
</span><span class=keyword>operator</span><span class=special>()(</span><span class=identifier>T0</span><span class=special>&amp; </span><span class=identifier>_0</span><span class=special>) </span><span class=keyword>const
</span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>unary_operator</span><span class=special>&lt;</span><span class=identifier>negative_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>&gt;::</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>_0</span><span class=special>); }
};
</span></pre></code>
<p>
Binary example:</p>
<code><pre>
<span class=keyword>struct </span><span class=identifier>plus_op </span><span class=special>{
</span><span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T0</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>T1</span><span class=special>&gt;
</span><span class=keyword>struct </span><span class=identifier>result </span><span class=special>{
</span><span class=keyword>typedef </span><span class=keyword>typename </span><span class=identifier>binary_operator</span><span class=special>&lt;</span><span class=identifier>plus_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt;
::</span><span class=identifier>result_type </span><span class=identifier>type</span><span class=special>;
};
</span><span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T0</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>T1</span><span class=special>&gt;
</span><span class=keyword>typename </span><span class=identifier>binary_operator</span><span class=special>&lt;</span><span class=identifier>plus_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt;::</span><span class=identifier>result_type
</span><span class=keyword>operator</span><span class=special>()(</span><span class=identifier>T0</span><span class=special>&amp; </span><span class=identifier>_0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&amp; </span><span class=identifier>_1</span><span class=special>) </span><span class=keyword>const
</span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>binary_operator</span><span class=special>&lt;</span><span class=identifier>plus_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt;::</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>_0</span><span class=special>, </span><span class=identifier>_1</span><span class=special>); }
};
</span></pre></code>
<p>
Notice that these are again perfect examples of a composite operation. This style of specialized function is ubiquitous in the framework. We shall see how the unary_operator&lt;negative_op, T0&gt; and the binary_operator&lt;plus_op, T0, T1&gt; template classes, work in a short while.</p>
<p>
Here are the complete list of operator tags:</p>
<code><pre>
<span class=comment>// Unary operator tags
</span><span class=keyword>struct </span><span class=identifier>negative_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>positive_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>logical_not_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>invert_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>reference_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>dereference_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>pre_incr_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>pre_decr_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>post_incr_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>post_decr_op</span><span class=special>;
// </span><span class=identifier>Binary </span><span class=keyword>operator </span><span class=identifier>tags
</span><span class=keyword>struct </span><span class=identifier>assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>index_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>plus_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>minus_assign_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>times_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>divide_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>mod_assign_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>and_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>or_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>xor_assign_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>shift_l_assign_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>shift_r_assign_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>plus_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>minus_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>times_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>divide_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>mod_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>and_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>or_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>xor_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>shift_l_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>shift_r_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>eq_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>not_eq_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>lt_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>lt_eq_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>gt_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>gt_eq_op</span><span class=special>;
</span><span class=keyword>struct </span><span class=identifier>logical_and_op</span><span class=special>; </span><span class=keyword>struct </span><span class=identifier>logical_or_op</span><span class=special>;
</span></pre></code>
<a name="unary_operator"></a><h3>unary_operator</h3><p>
The unary_operator class implements most of the C++ unary operators. Each specialization is basically a simple static eval function plus a result_type typedef that determines the return type of the eval function.</p>
<p>
TagT is one of the unary operator tags above and T is the data type (argument) involved in the operation. Here is an example:</p>
<code><pre>
<span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T</span><span class=special>&gt;
</span><span class=keyword>struct </span><span class=identifier>unary_operator</span><span class=special>&lt;</span><span class=identifier>negative_op</span><span class=special>, </span><span class=identifier>T</span><span class=special>&gt; {
</span><span class=keyword>typedef </span><span class=identifier>T </span><span class=keyword>const </span><span class=identifier>result_type</span><span class=special>;
</span><span class=keyword>static </span><span class=identifier>result_type </span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>T </span><span class=keyword>const</span><span class=special>&amp; </span><span class=identifier>v</span><span class=special>)
{ </span><span class=keyword>return </span><span class=special>-</span><span class=identifier>v</span><span class=special>; }
};
</span></pre></code>
<p>
This example is exactly what was being referred to by the first example we saw in the section on operator tags.</p>
<p>
Only the behavior of C/C++ built-in types are taken into account in the specializations provided in operator.hpp. For user-defined types, these specializations may still be used provided that the operator overloads of such types adhere to the standard behavior of built-in types.</p>
<p>
A separate special_ops.hpp file implements more STL savvy specializations. Other more specialized unary_operator implementations may be defined by the client for specific unary operator tags/data types.</p>
<a name="binary_operator"></a><h3>binary_operator</h3><p>
The binary_operator class implements most of the C++ binary operators. Each specialization is basically a simple static eval function plus a result_type typedef that determines the return type of the eval function.</p>
<p>
TagT is one of the binary operator tags above T0 and T1 are the (arguments') data types involved in the operation. Here is an example:</p>
<code><pre>
<span class=keyword>template </span><span class=special>&lt;</span><span class=keyword>typename </span><span class=identifier>T0</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>T1</span><span class=special>&gt;
</span><span class=keyword>struct </span><span class=identifier>binary_operator</span><span class=special>&lt;</span><span class=identifier>plus_op</span><span class=special>, </span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt; {
</span><span class=keyword>typedef </span><span class=keyword>typename </span><span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt;::</span><span class=identifier>type </span><span class=keyword>const </span><span class=identifier>result_type</span><span class=special>;
</span><span class=keyword>static </span><span class=identifier>result_type </span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>T0 </span><span class=keyword>const</span><span class=special>&amp; </span><span class=identifier>lhs</span><span class=special>, </span><span class=identifier>T1 </span><span class=keyword>const</span><span class=special>&amp; </span><span class=identifier>rhs</span><span class=special>)
{ </span><span class=keyword>return </span><span class=identifier>lhs </span><span class=special>+ </span><span class=identifier>rhs</span><span class=special>; }
};
</span></pre></code>
<p>
This example is exactly what was being referred to by the second example we saw in the section on operator tags. higher_rank&lt;T0, T1&gt; is a type computer. We shall see how this works in a short while, pardon the forward information.</p>
<p>
Only the behavior of C/C++ built-in types are taken into account in the specializations provided in operator.hpp. For user-defined types, these specializations may still be used provided that the operator overloads of such types adhere to the standard behavior of built-in types.</p>
<p>
A separate special_ops.hpp file implements more STL savvy specializations. Other more specialized unary_operator implementations may be defined by the client for specific unary operator tags/data types.</p>
<p>
All binary_operator except the logical_and_op and logical_or_op have an eval static function that carries out the actual operation. The logical_and_op and logical_or_op d are special because these two operators are short-circuit evaluated.</p>
<table width="80%" border="0" align="center">
<tr>
<td class="note_box">
<img src="theme/lens.gif"></img> <b>Short Circuiting || and &amp;&amp;</b><br><br>The logical_and_op and logical_or_op are special due to the C/C++ short circuiting rule, i.e. a || b and a &amp;&amp; b are short circuit evaluated. A forwarding operation cannot be used because all function arguments are evaluated before a function is called. logical_and_op and logical_or_op are specialized composites with implied operations. </td>
</tr>
</table>
<a name="rank"></a><h2>rank</h2><p>
rank&lt;T&gt; class has a static int constant 'value' that defines the absolute rank of a type. rank&lt;T&gt; is used to choose the result type of binary operators such as +. The type with the higher rank wins and is used as the operator's return type. A generic user defined type has a very high rank and always wins when compared against a user defined type. If this is not desireable, one can write a rank specialization for the type. Here are some predefined examples:</p>
<code><pre>
<span class=keyword>template </span><span class=special>&lt;&gt; </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special>&lt;</span><span class=keyword>char</span><span class=special>&gt; { </span><span class=keyword>static </span><span class=keyword>int </span><span class=keyword>const </span><span class=identifier>value </span><span class=special>= </span><span class=number>20</span><span class=special>; };
</span><span class=keyword>template </span><span class=special>&lt;&gt; </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special>&lt;</span><span class=keyword>signed </span><span class=keyword>char</span><span class=special>&gt; { </span><span class=keyword>static </span><span class=keyword>int </span><span class=keyword>const </span><span class=identifier>value </span><span class=special>= </span><span class=number>20</span><span class=special>; };
</span><span class=keyword>template </span><span class=special>&lt;&gt; </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special>&lt;</span><span class=keyword>unsigned </span><span class=keyword>char</span><span class=special>&gt; { </span><span class=keyword>static </span><span class=keyword>int </span><span class=keyword>const </span><span class=identifier>value </span><span class=special>= </span><span class=number>30</span><span class=special>; };
</span><span class=keyword>template </span><span class=special>&lt;&gt; </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special>&lt;</span><span class=keyword>wchar_t</span><span class=special>&gt; { </span><span class=keyword>static </span><span class=keyword>int </span><span class=keyword>const </span><span class=identifier>value </span><span class=special>= </span><span class=number>40</span><span class=special>; };
</span></pre></code>
<p>
Take note that ranks 0..9999 are reserved by the framework.</p>
<p>
A template type computer higher_rank&lt;T0, T1&gt; chooses the type (T0 or T1) with the higher rank. We saw in the binary_operator for plus_op how it was used. Specifically:</p>
<code><pre>
<span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>T0</span><span class=special>, </span><span class=identifier>T1</span><span class=special>&gt;::</span><span class=identifier>type
</span></pre></code>
<p>
returns either T0 or T1 depending on which has a higher rank. In some operator applications such as a + b, the result is actually the one with the higher rank. For example if a is of type int and b is of type double, the result will be of type double. This facility can also be quite useful for evaluating some functions. For instance if we have a sum(a, b, c, d, e) function, we can call this type computer to get the type with the highest rank:</p>
<code><pre>
<span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>TA</span><span class=special>,
</span><span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>TB</span><span class=special>,
</span><span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>TC</span><span class=special>,
</span><span class=identifier>higher_rank</span><span class=special>&lt;</span><span class=identifier>TD</span><span class=special>, </span><span class=identifier>TE</span><span class=special>&gt;::</span><span class=identifier>type
</span><span class=special>&gt;::</span><span class=identifier>type
</span><span class=special>&gt;::</span><span class=identifier>type
</span><span class=special>&gt;::</span><span class=identifier>type
</span></pre></code>
<table width="80%" border="0" align="center">
<tr>
<td class="note_box">
<img src="theme/alert.gif"></img> When used within templates, be sure to use 'typename' appropriately. See binary_operator&lt;plus_op, T0, T1&gt; above. </td>
</tr>
</table>
<table border="0">
<tr>
<td width="30"><a href="../index.html"><img src="theme/u_arr.gif" border="0"></a></td>
<td width="30"><a href="composites_revisited.html"><img src="theme/l_arr.gif" border="0"></a></td>
<td width="20"><a href="interfacing.html"><img src="theme/r_arr.gif" border="0"></a></td>
</tr>
</table>
<br>
<hr size="1">
<p class="copyright">Copyright &copy; 2001-2002 Joel de Guzman<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>
</body>
</html>