403 lines
46 KiB
HTML
403 lines
46 KiB
HTML
<html>
|
|
<head>
|
|
<!-- Generated by the Spirit (http://spirit.sf.net) QuickDoc -->
|
|
<title>Interfacing</title>
|
|
<link rel="stylesheet" href="theme/style.css" type="text/css">
|
|
<link rel="prev" href="operators_revisited.html">
|
|
<link rel="next" href="wrap_up.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>Interfacing</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="operators_revisited.html"><img src="theme/l_arr.gif" border="0"></a></td>
|
|
<td width="20"><a href="wrap_up.html"><img src="theme/r_arr.gif" border="0"></a></td>
|
|
</tr>
|
|
</table>
|
|
<p>
|
|
The modular design of Phoenix makes it extremely extensible. We have seen that layer upon layer, the whole framework is built on a solid foundation. There are only a few simple well designed concepts that are laid out like bricks. Overall the framework is designed to be extended. Everything above the composite and primitives can in fact be considered just as extensions to the framework. This modular design was inherited from the <a href="http://spirit.sourceforge.net">
|
|
Spirit</a> inline parser framework.</p>
|
|
<p>
|
|
Extension is non-intrusive. And, whenever a component or module is extended, the new extension automatically becomes a first class citizen and is automatically recognized by all modules and components in the framework. There are a multitude of ways in which a module is extended.</p>
|
|
<p>
|
|
1) Write and deploy a new primitive:</p>
|
|
<p>
|
|
So far we have presented only a few primitives 1) arguments 2) values and 3) variables. For the sake of illustration, let us write a simple primitive extension. Let us call it static_int. It shall be parameterized by an integer value. It is like a static version of the value<int> class, but since it is static, holds no data at all. The integer is encoded in its type. Here is the complete class (sample5.cpp):</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>int </span><span class=identifier>N</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>static_int </span><span class=special>{
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>result </span><span class=special>{ </span><span class=keyword>typedef </span><span class=keyword>int </span><span class=identifier>type</span><span class=special>; };
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</span><span class=keyword>int </span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>TupleT </span><span class=keyword>const</span><span class=special>&) </span><span class=keyword>const </span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>N</span><span class=special>; }
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
That's it. Done! Now we can use this as it is already a full- fledged Phoenix citizen due to interface conformance. Let us write a suitable generator to make it easier to use our static_int. Remember that it should be wrapped as an actor before it can be used. Let us call our generator int_const:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>int </span><span class=identifier>N</span><span class=special>>
|
|
</span><span class=identifier>phoenix</span><span class=special>::</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>static_int</span><span class=special><</span><span class=identifier>N</span><span class=special>> >
|
|
</span><span class=identifier>int_const</span><span class=special>()
|
|
{
|
|
</span><span class=keyword>return </span><span class=identifier>static_int</span><span class=special><</span><span class=identifier>N</span><span class=special>>();
|
|
}
|
|
</span></pre></code>
|
|
<p>
|
|
Now we are done. Let's use it:</p>
|
|
<code><pre>
|
|
<span class=identifier>cout </span><span class=special><< (</span><span class=identifier>int_const</span><span class=special><</span><span class=number>5</span><span class=special>>() + </span><span class=identifier>int_const</span><span class=special><</span><span class=number>6</span><span class=special>>())() << </span><span class=identifier>endl</span><span class=special>;
|
|
</span></pre></code>
|
|
<p>
|
|
Prints out "11". There are lots of things you can do with this form of extension. For instance, data type casts come to mind. Example:</p>
|
|
<code><pre>
|
|
<span class=identifier>lazy_cast</span><span class=special><</span><span class=identifier>T</span><span class=special>>(</span><span class=identifier>some_lazy_expression</span><span class=special>)
|
|
</span></pre></code>
|
|
<p>
|
|
2) Write and deploy a new composite:</p>
|
|
<p>
|
|
This is more complicated than our first example (writing a primitive). Nevertheless, once you get the basics, writing a composite is almost mechanical and boring (read: easy <img src="theme/smiley.gif"></img>). Check out statements.hpp. All the lazy statements are written in terms of the composite interface.</p>
|
|
<p>
|
|
Ok, let's get on with it. Recall that the if_ else_ lazy statement (and all statements for that matter) return void. What's missing, and will surely be useful, is something like C/C++'s "cond ? a : b" expression. It is really unfortunate that C++ fell short of allowing this to be overloaded. Sigh. Anyway here's the code (sample6.cpp):</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>if_else_composite </span><span class=special>{
|
|
|
|
</span><span class=keyword>typedef </span><span class=identifier>if_else_composite</span><span class=special><</span><span class=identifier>CondT</span><span class=special>, </span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>FalseT</span><span class=special>> </span><span class=identifier>self_t</span><span class=special>;
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</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>higher_rank</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>plain_type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>plain_type
|
|
</span><span class=special>>::</span><span class=identifier>type </span><span class=identifier>type</span><span class=special>;
|
|
};
|
|
|
|
</span><span class=identifier>if_else_composite</span><span class=special>(
|
|
</span><span class=identifier>CondT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>cond_</span><span class=special>, </span><span class=identifier>TrueT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>true__</span><span class=special>, </span><span class=identifier>FalseT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>false__</span><span class=special>)
|
|
: </span><span class=identifier>cond</span><span class=special>(</span><span class=identifier>cond_</span><span class=special>), </span><span class=identifier>true_</span><span class=special>(</span><span class=identifier>true__</span><span class=special>), </span><span class=identifier>false_</span><span class=special>(</span><span class=identifier>false__</span><span class=special>) {}
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>self_t</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>type
|
|
</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>TupleT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>args</span><span class=special>) </span><span class=keyword>const
|
|
</span><span class=special>{
|
|
</span><span class=keyword>return </span><span class=identifier>cond</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>) ? </span><span class=identifier>true_</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>) : </span><span class=identifier>false_</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>);
|
|
}
|
|
|
|
</span><span class=identifier>CondT </span><span class=identifier>cond</span><span class=special>; </span><span class=identifier>TrueT </span><span class=identifier>true_</span><span class=special>; </span><span class=identifier>FalseT </span><span class=identifier>false_</span><span class=special>; // </span><span class=identifier>actors
|
|
</span><span class=special>};
|
|
</span></pre></code>
|
|
<p>
|
|
Ok, this is quite a mouthfull. Let's digest this piecemeal.</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>if_else_composite </span><span class=special>{
|
|
</span></pre></code>
|
|
<p>
|
|
This is basically a specialized composite that has 3 actors. It has no operation since it is implied. The 3 actors are cond (condition of type CondT) true_ (the true branch of type TrueT), false_ the (false branch or type FalseT).</p>
|
|
<code><pre>
|
|
<span class=keyword>typedef </span><span class=identifier>if_else_composite</span><span class=special><</span><span class=identifier>CondT</span><span class=special>, </span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>FalseT</span><span class=special>> </span><span class=identifier>self_t</span><span class=special>;
|
|
</span></pre></code>
|
|
<p>
|
|
self_t is a typedef that declares its own type: "What am I?"</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</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>higher_rank</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>plain_type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>plain_type
|
|
</span><span class=special>>::</span><span class=identifier>type </span><span class=identifier>type</span><span class=special>;
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
We have seen result before. For actor base-classes such as composites and primitives, the parameter is a TupleT, i.e. the tupled arguments passed in from the actor.</p>
|
|
<p>
|
|
So given some arguments, what will be our return type? TrueT and FalseT are also actors remember? So first, we should ask them "What are your *plain* (stripped from references) return types?"</p>
|
|
<p>
|
|
Knowing that, our task is then to know which type has a higher rank (recall rank<T> and higher_rank<T0, T1>). Why do we have to do this? We are emulating the behavior of the "cond ? a : b" expression. In C/C++, the type of this expression is the one (a or b) with the higher rank. For example, if a is an int and b is a double, the result should be a double.</p>
|
|
<p>
|
|
Following this, finally, we have a return type typedef'd by result<TupleT>::type.</p>
|
|
<code><pre>
|
|
<span class=identifier>if_else_composite</span><span class=special>(
|
|
</span><span class=identifier>CondT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>cond_</span><span class=special>, </span><span class=identifier>TrueT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>true__</span><span class=special>, </span><span class=identifier>FalseT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>false__</span><span class=special>)
|
|
: </span><span class=identifier>cond</span><span class=special>(</span><span class=identifier>cond_</span><span class=special>), </span><span class=identifier>true_</span><span class=special>(</span><span class=identifier>true__</span><span class=special>), </span><span class=identifier>false_</span><span class=special>(</span><span class=identifier>false__</span><span class=special>) {}
|
|
</span></pre></code>
|
|
<p>
|
|
This is our constructor. We just stuff the constructor arguments into our member variables.</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>TupleT</span><span class=special>>
|
|
</span><span class=keyword>typename </span><span class=identifier>actor_result</span><span class=special><</span><span class=identifier>self_t</span><span class=special>, </span><span class=identifier>TupleT</span><span class=special>>::</span><span class=identifier>type
|
|
</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>TupleT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>args</span><span class=special>) </span><span class=keyword>const
|
|
</span></pre></code>
|
|
<p>
|
|
Now, here is our main eval member function. Given a self_t, our type, and the TupleT, the return type deduction is almost canonical. Just ask actor_result, it'll surely know.</p>
|
|
<code><pre>
|
|
<span class=special>{
|
|
</span><span class=keyword>return </span><span class=identifier>cond</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>) ? </span><span class=identifier>true_</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>) : </span><span class=identifier>false_</span><span class=special>.</span><span class=identifier>eval</span><span class=special>(</span><span class=identifier>args</span><span class=special>);
|
|
}
|
|
</span></pre></code>
|
|
<p>
|
|
We pass the tupled args to all of our actors: cond, args and args appropriately. Notice how this expression reflects the C/C++ version almost to the letter.</p>
|
|
<p>
|
|
Well that's it. Now let's write a generator for this composite:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>if_else_composite</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>> >
|
|
</span><span class=identifier>if_else_</span><span class=special>(</span><span class=identifier>CondT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>cond</span><span class=special>, </span><span class=identifier>TrueT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>true_</span><span class=special>, </span><span class=identifier>FalseT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>false_</span><span class=special>)
|
|
{
|
|
</span><span class=keyword>typedef </span><span class=identifier>if_else_composite</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>>
|
|
</span><span class=identifier>result</span><span class=special>;
|
|
|
|
</span><span class=keyword>return </span><span class=identifier>result</span><span class=special>(
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>cond</span><span class=special>),
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>true_</span><span class=special>),
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>false_</span><span class=special>));
|
|
}
|
|
</span></pre></code>
|
|
<p>
|
|
Now this should be trivial to explain. I hope. Again, let's digest this piecemeal.</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</span></pre></code>
|
|
<p>
|
|
Again, there are three elements involved: The CondT condition 'cond', the TrueT true branch 'true_, and the FalseT false branch 'false_'.</p>
|
|
<code><pre>
|
|
<span class=identifier>actor</span><span class=special><</span><span class=identifier>if_else_composite</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>> >
|
|
</span></pre></code>
|
|
<p>
|
|
This is our target. We want to generate this actor. Now, given our arguments (cond, true_ and false_), we are not really sure if they are really actors. What if the user passes the boolean true as the cond? Surely, that has to be converted to an actor<value<bool> >, otherwise Phoenix will go berzerk and will not be able to accommodate this alien.</p>
|
|
<code><pre>
|
|
<span class=identifier>as_actor</span><span class=special><</span><span class=identifier>T</span><span class=special>>::</span><span class=identifier>type
|
|
</span></pre></code>
|
|
<p>
|
|
is just what we need. This type computer converts from an arbitrary type T to a full-fledged actor citizen.</p>
|
|
<code><pre>
|
|
<span class=identifier>if_else_</span><span class=special>(</span><span class=identifier>CondT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>cond</span><span class=special>, </span><span class=identifier>TrueT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>true_</span><span class=special>, </span><span class=identifier>FalseT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>false_</span><span class=special>)
|
|
</span></pre></code>
|
|
<p>
|
|
These are the arguments to our generator 'if_else_'.</p>
|
|
<code><pre>
|
|
<span class=keyword>typedef </span><span class=identifier>if_else_composite</span><span class=special><
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>,
|
|
</span><span class=keyword>typename </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type</span><span class=special>>
|
|
</span><span class=identifier>result</span><span class=special>;
|
|
</span></pre></code>
|
|
<p>
|
|
Same as before, this is our target return type, this time stripped off the actor. That's OK because the actor<T> has a constructor that takes in a BaseT object: 'result' in this case.</p>
|
|
<code><pre>
|
|
<span class=keyword>return </span><span class=identifier>result</span><span class=special>(
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>CondT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>cond</span><span class=special>),
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>true_</span><span class=special>),
|
|
</span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>false_</span><span class=special>));
|
|
</span></pre></code>
|
|
<p>
|
|
Finally, we construct and return our result. Notice how we called the as_actor<T>::convert static function to do the conversion from T to a full-fledged actor for each of the arguments.</p>
|
|
<p>
|
|
At last. Now we can use our brand new composite and its generator:</p>
|
|
<code><pre>
|
|
<span class=comment>// Print all contents of an STL container c and
|
|
// prefix " is odd" or " is even" appropriately.
|
|
|
|
</span><span class=identifier>for_each</span><span class=special>(</span><span class=identifier>c</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(), </span><span class=identifier>c</span><span class=special>.</span><span class=identifier>end</span><span class=special>(),
|
|
</span><span class=identifier>cout
|
|
</span><span class=special><< </span><span class=identifier>arg1
|
|
</span><span class=special><< </span><span class=identifier>if_else_</span><span class=special>(</span><span class=identifier>arg1 </span><span class=special>% </span><span class=number>2 </span><span class=special>== </span><span class=number>1</span><span class=special>, </span><span class=string>" is odd"</span><span class=special>, </span><span class=string>" is even"</span><span class=special>)
|
|
<< </span><span class=identifier>val</span><span class=special>(</span><span class=literal>'\n'</span><span class=special>)
|
|
);
|
|
</span></pre></code>
|
|
<p>
|
|
3) Write an as_actor<T> converter for a specific type:</p>
|
|
<p>
|
|
By default, an unknown type T is converted to an actor<value<T> >. Say we just wrote a special primitive my_lazy_class following example 1. Whenever we have an object of type my_class, we want to convert this to a my_lazy_class automatically.</p>
|
|
<p>
|
|
as_actor<T> is Phoenix's type converter. All facilities that need to convert from an unknown type to an actor passes through this class. Specializing as_actor<T> for my_class is just what we need. For example:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><>
|
|
</span><span class=keyword>struct </span><span class=identifier>as_actor</span><span class=special><</span><span class=identifier>my_class</span><span class=special>> {
|
|
|
|
</span><span class=keyword>typedef </span><span class=identifier>actor</span><span class=special><</span><span class=identifier>my_lazy_class</span><span class=special>> </span><span class=identifier>type</span><span class=special>;
|
|
</span><span class=keyword>static </span><span class=identifier>type </span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>my_class </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>x</span><span class=special>)
|
|
{ </span><span class=keyword>return </span><span class=identifier>my_lazy_class</span><span class=special>(</span><span class=identifier>x</span><span class=special>); }
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
For reference, here is the main is_actor<T> interface:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>T</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>as_actor </span><span class=special>{
|
|
|
|
</span><span class=keyword>typedef </span><span class=special>??? </span><span class=identifier>type</span><span class=special>;
|
|
</span><span class=keyword>static </span><span class=identifier>type </span><span class=identifier>convert</span><span class=special>(</span><span class=identifier>T </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>x</span><span class=special>);
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
where ??? is the actor type returned by the static convert function. By default, this is:</p>
|
|
<code><pre>
|
|
<span class=keyword>typedef </span><span class=identifier>value</span><span class=special><</span><span class=identifier>T</span><span class=special>> </span><span class=identifier>type</span><span class=special>;
|
|
</span></pre></code>
|
|
<p>
|
|
4) Write a specialized overloaded operator for a specific type:</p>
|
|
<p>
|
|
Consider the handling of operator << std::ostream such as cout. When we see an expression such as:</p>
|
|
<code><pre>
|
|
<span class=identifier>cout </span><span class=special><< </span><span class=string>"Hello World\n"
|
|
</span></pre></code>
|
|
<p>
|
|
the operator overload actually takes in cout by reference, modifies it and returns the same cout again by reference. This does not conform to the standard behavior of the shift left operator for built-in ints.</p>
|
|
<p>
|
|
In such cases, we can provide a specialized overload for this to work as a lazy-operator in expressions such as "cout << arg1 << arg2;" where the operatior behavior deviates from the standard operator:</p>
|
|
<ol><li>std::ostream is taken as the LHS by reference</li><li>std::ostream is converted to an actor<variable<std::ostream> > instead of the default actor<value<std::ostream> >.</li></ol><p>
|
|
We supply a special overload then (see special_ops.hpp):</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>BaseT</span><span class=special>>
|
|
</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>composite</span><span class=special><
|
|
</span><span class=identifier>shift_l_op</span><span class=special>, // </span><span class=identifier>an </span><span class=keyword>operator </span><span class=identifier>tag
|
|
</span><span class=identifier>variable</span><span class=special><</span><span class=identifier>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>>, // </span><span class=identifier>an </span><span class=identifier>actor </span><span class=identifier>LHS
|
|
</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>BaseT</span><span class=special>>, // </span><span class=identifier>an </span><span class=identifier>actor </span><span class=identifier>RHS
|
|
</span><span class=special>> >
|
|
</span><span class=keyword>operator</span><span class=special><<(
|
|
</span><span class=identifier>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>& </span><span class=identifier>_0</span><span class=special>, // </span><span class=identifier>LHS </span><span class=identifier>argument
|
|
</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>BaseT</span><span class=special>> </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>_1</span><span class=special>) // </span><span class=identifier>RHS </span><span class=identifier>argument
|
|
</span><span class=special>{
|
|
</span><span class=keyword>return </span><span class=identifier>actor</span><span class=special><</span><span class=identifier>composite</span><span class=special><
|
|
</span><span class=identifier>shift_l_op</span><span class=special>, // </span><span class=identifier>an </span><span class=keyword>operator </span><span class=identifier>tag
|
|
</span><span class=identifier>variable</span><span class=special><</span><span class=identifier>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>>, // </span><span class=identifier>an </span><span class=identifier>actor </span><span class=identifier>LHS
|
|
</span><span class=identifier>actor</span><span class=special><</span><span class=identifier>BaseT</span><span class=special>>, // </span><span class=identifier>an </span><span class=identifier>actor </span><span class=identifier>RHS
|
|
</span><span class=special>> >(</span><span class=identifier>var</span><span class=special>(</span><span class=identifier>_0</span><span class=special>), </span><span class=identifier>_1</span><span class=special>); // </span><span class=identifier>construct </span>#<span class=identifier>em
|
|
</span><span class=special>}
|
|
</span></pre></code>
|
|
<p>
|
|
Take note that the std::ostream reference is converted to a actor<variable<std::ostream> > instead of the default actor<value<std::ostream> > which is not appropriate in this case.</p>
|
|
<p>
|
|
This is not yet complete. Take note also that a specialization for binary_operator also needs to be written (see no. 6).</p>
|
|
<p>
|
|
5) Specialize a rank<T> for a specific type or group of types:</p>
|
|
<p>
|
|
Scenario: We have a set of more specialized numeric classes with higher precision than the built-in types. We have integer, floating and rational classes. All of the classes allow type promotions from the built-ins. These classes have all the pertinent operators implemented along with a couple of mixed type operators whenever appropriate. The operators conform to the canonical behavior of the built-in types. We want to enable Phoenix support for our numeric classes.</p>
|
|
<p>
|
|
Solution: Write rank specializations for our numeric types. This is trivial and straightforward:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><> </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special><</span><span class=identifier>integer</span><span class=special>> { </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>10000</span><span class=special>; };
|
|
</span><span class=keyword>template </span><span class=special><> </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special><</span><span class=identifier>floating</span><span class=special>> { </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>10020</span><span class=special>; };
|
|
</span><span class=keyword>template </span><span class=special><> </span><span class=keyword>struct </span><span class=identifier>rank</span><span class=special><</span><span class=identifier>rational</span><span class=special>> { </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>10030</span><span class=special>; };
|
|
</span></pre></code>
|
|
<p>
|
|
Now, whenever there are mixed-type operations such as a + b where a is a primitive built-in int and b is our rational class, the correct promotion will be applied, and the result will be a rational. The type with the higher rank will win.</p>
|
|
<p>
|
|
6) Specialize a unary_operator<TagT, T> or binary_operator<TagT, T0, T1> for a specific type:</p>
|
|
<p>
|
|
Scenario: We have a non-STL conforming iterator named my_iterator. Fortunately, its ++ operator works as expected. Unfortunately, when applying the dereference operator *p, it returns an object of type my_class but does not follow STL's convention that iterator classes have a typedef named reference.</p>
|
|
<p>
|
|
Solution, write a unary_operator specialization for our non- standard class:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><>
|
|
</span><span class=keyword>struct </span><span class=identifier>unary_operator</span><span class=special><</span><span class=identifier>dereference_op</span><span class=special>, </span><span class=identifier>my_iterator</span><span class=special>> {
|
|
|
|
</span><span class=keyword>typedef </span><span class=identifier>my_class </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>my_iterator </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>iter</span><span class=special>)
|
|
{ </span><span class=keyword>return </span><span class=special>*</span><span class=identifier>iter</span><span class=special>; }
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
Scenario: We have a legacy bigint implementation that we use for cryptography. The class design is totally brain-dead and disobeys all the rules. For example, its + operator is destructive and actually applies the += semantics for efficiency (yes, there are such brain-dead beasts!).</p>
|
|
<p>
|
|
Solution: write a binary_operator specialization for our non- standard class:</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><>
|
|
</span><span class=keyword>struct </span><span class=identifier>binary_operator</span><span class=special><</span><span class=identifier>plus_op</span><span class=special>, </span><span class=identifier>bigint</span><span class=special>, </span><span class=identifier>bigint</span><span class=special>> {
|
|
|
|
</span><span class=keyword>typedef </span><span class=identifier>bigint</span><span class=special>& </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>bigint</span><span class=special>& </span><span class=identifier>lhs</span><span class=special>, </span><span class=identifier>bigint </span><span class=keyword>const</span><span class=special>& </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>
|
|
Going back to our example in no. 4, we also need to write a binary_operator<TagT, T0, T1> specialization for ostreams because the << operator for ostreams deviate from the normal behavior.</p>
|
|
<code><pre>
|
|
<span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>T1</span><span class=special>>
|
|
</span><span class=keyword>struct </span><span class=identifier>binary_operator</span><span class=special><</span><span class=identifier>shift_l_op</span><span class=special>, </span><span class=identifier>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>, </span><span class=identifier>T1</span><span class=special>> {
|
|
|
|
</span><span class=keyword>typedef </span><span class=identifier>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>& </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>std</span><span class=special>::</span><span class=identifier>ostream</span><span class=special>& </span><span class=identifier>out</span><span class=special>, </span><span class=identifier>T1 </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>rhs</span><span class=special>)
|
|
{ </span><span class=keyword>return </span><span class=identifier>out </span><span class=special><< </span><span class=identifier>rhs</span><span class=special>; }
|
|
};
|
|
</span></pre></code>
|
|
<p>
|
|
7) Simply write a lazy-function.</p>
|
|
<p>
|
|
Consider this:</p>
|
|
<code><pre>
|
|
<span class=keyword>struct </span><span class=identifier>if_else_func </span><span class=special>{
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</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>higher_rank</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type </span><span class=identifier>type</span><span class=special>;
|
|
};
|
|
|
|
</span><span class=keyword>template </span><span class=special><</span><span class=keyword>typename </span><span class=identifier>CondT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>TrueT</span><span class=special>, </span><span class=keyword>typename </span><span class=identifier>FalseT</span><span class=special>>
|
|
</span><span class=keyword>typename </span><span class=identifier>higher_rank</span><span class=special><</span><span class=identifier>TrueT</span><span class=special>, </span><span class=identifier>FalseT</span><span class=special>>::</span><span class=identifier>type
|
|
</span><span class=keyword>operator</span><span class=special>()(</span><span class=identifier>CondT </span><span class=identifier>cond</span><span class=special>, </span><span class=identifier>TrueT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>t</span><span class=special>, </span><span class=identifier>FalseT </span><span class=keyword>const</span><span class=special>& </span><span class=identifier>f</span><span class=special>) </span><span class=keyword>const
|
|
</span><span class=special>{ </span><span class=keyword>return </span><span class=identifier>cond </span><span class=special>? </span><span class=identifier>t </span><span class=special>: </span><span class=identifier>f</span><span class=special>; }
|
|
};
|
|
|
|
</span><span class=identifier>function</span><span class=special><</span><span class=identifier>if_else_func</span><span class=special>> </span><span class=identifier>if_else_</span><span class=special>;
|
|
</span></pre></code>
|
|
<p>
|
|
And this corresponding usage:</p>
|
|
<code><pre>
|
|
<span class=comment>// Print all contents of an STL container c and
|
|
// prefix " is odd" or " is even" appropriately.
|
|
|
|
</span><span class=identifier>for_each</span><span class=special>(</span><span class=identifier>c</span><span class=special>.</span><span class=identifier>begin</span><span class=special>(), </span><span class=identifier>c</span><span class=special>.</span><span class=identifier>end</span><span class=special>(),
|
|
</span><span class=identifier>cout
|
|
</span><span class=special><< </span><span class=identifier>arg1
|
|
</span><span class=special><< </span><span class=identifier>if_else_</span><span class=special>(</span><span class=identifier>arg1 </span><span class=special>% </span><span class=number>2 </span><span class=special>== </span><span class=number>1</span><span class=special>, </span><span class=string>" is odd"</span><span class=special>, </span><span class=string>" is even"</span><span class=special>)
|
|
<< </span><span class=identifier>val</span><span class=special>(</span><span class=literal>'\n'</span><span class=special>)
|
|
);
|
|
</span></pre></code>
|
|
<p>
|
|
What the $%^!? If we can do this, why on earth did we go to all the trouble twisting our brains inside out with the if_else_ composite in no. 2? Hey, not so fast, there's a slight difference that justifies the if_else_ composite: It is not apparent in the example, but the composite version of the if_else_ evaluates either the true or the false branch, **but not both**. The lazy-function version above always eagerly evaluates all its arguments before the function is called. Thus, if we are to adhere strongly to C/C++ semantics, we need the composite version.</p>
|
|
<p>
|
|
Besides, I need to show an example... Hmmm, so what's the point of no. 7 then? Well, in most cases, a lazy-function will suffice. These beasts are quite powerful, you know.</p>
|
|
<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="operators_revisited.html"><img src="theme/l_arr.gif" border="0"></a></td>
|
|
<td width="20"><a href="wrap_up.html"><img src="theme/r_arr.gif" border="0"></a></td>
|
|
</tr>
|
|
</table>
|
|
<br>
|
|
<hr size="1">
|
|
<p class="copyright">Copyright © 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>
|