Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_33_1/libs/mpl/doc/tutorial/implementing.html @ 12

Last change on this file since 12 was 12, checked in by landauf, 18 years ago

added boost

File size: 13.5 KB
Line 
1<?xml version="1.0" encoding="utf-8" ?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
4<head>
5<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
6<meta name="generator" content="Docutils 0.3.6: http://docutils.sourceforge.net/" />
7<title>THE BOOST MPL LIBRARY: Implementing Multiplication</title>
8<link rel="stylesheet" href="../style.css" type="text/css" />
9</head>
10<body class="docframe">
11<table class="header"><tr class="header"><td class="header-group navigation-bar"><span class="navigation-group"><a href="./implementing-addition-and.html" class="navigation-link">Prev</a>&nbsp;<a href="./implementing-division.html" class="navigation-link">Next</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./implementing-addition-and.html" class="navigation-link">Back</a>&nbsp;<a href="./implementing-division.html" class="navigation-link">Along</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./dimensional-analysis.html" class="navigation-link">Up</a>&nbsp;<a href="../index.html" class="navigation-link">Home</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./tutorial_toc.html" class="navigation-link">Full TOC</a></span></td>
12<td class="header-group page-location"><a href="../index.html" class="navigation-link">Front Page</a> / <a href="./tutorial-metafunctions.html" class="navigation-link">Tutorial: Metafunctions and Higher-Order Metaprogramming</a> / <a href="./dimensional-analysis.html" class="navigation-link">Dimensional Analysis</a> / <a href="./implementing.html" class="navigation-link">Implementing Multiplication</a></td>
13</tr></table><div class="header-separator"></div>
14<div class="section" id="implementing">
15<h1><a class="toc-backref" href="./dimensional-analysis.html#id45" name="implementing">Implementing Multiplication</a></h1>
16<p>Multiplication is a bit more complicated than addition and
17subtraction.  So far, the dimensions of the arguments and results have
18all been identical, but when multiplying, the result will usually
19have different dimensions from either of the arguments.  For
20multiplication, the relation:</p>
21<blockquote>
22(<em>x</em><sup>a</sup>)(<em>x</em><sup>b</sup>) == <em>x</em> <sup>(a + b)</sup></blockquote>
23<!-- @litre_translator.line_offset -= 7 -->
24<p>implies that the exponents of the result dimensions should be the
25sum of corresponding exponents from the argument
26dimensions. Division is similar, except that the sum is replaced by
27a difference.</p>
28<p>To combine corresponding elements from two sequences, we'll use
29MPL's <tt class="literal"><span class="pre">transform</span></tt> algorithm.  <tt class="literal"><span class="pre">transform</span></tt> is a metafunction
30that iterates through two input sequences in parallel, passing an
31element from each sequence to an arbitrary binary metafunction, and
32placing the result in an output sequence.</p>
33<pre class="literal-block">
34template &lt;class Sequence1, class Sequence2, class BinaryOperation&gt;
35struct transform;  // returns a Sequence
36</pre>
37<p>The signature above should look familiar if you're acquainted with the
38STL <tt class="literal"><span class="pre">transform</span></tt> algorithm that accepts two <em>runtime</em> sequences
39as inputs:</p>
40<pre class="literal-block">
41template &lt;
42    class InputIterator1, class InputIterator2
43  , class OutputIterator, class BinaryOperation
44&gt;
45void transform(
46    InputIterator1 start1, InputIterator2 finish1
47  , InputIterator2 start2
48  , OutputIterator result, BinaryOperation func);
49</pre>
50<!-- @ example.wrap('namespace shield{','}')
51compile() -->
52<p>Now we just need to pass a <tt class="literal"><span class="pre">BinaryOperation</span></tt> that adds or
53subtracts in order to multiply or divide dimensions with
54<tt class="literal"><span class="pre">mpl::transform</span></tt>.  If you look through the <a class="reference" href="./reference-manual.html">the MPL reference manual</a>, you'll
55come across <tt class="literal"><span class="pre">plus</span></tt> and <tt class="literal"><span class="pre">minus</span></tt> metafunctions that do just what
56you'd expect:</p>
57<pre class="literal-block">
58#include &lt;boost/static_assert.hpp&gt;
59#include &lt;boost/mpl/plus.hpp&gt;
60#include &lt;boost/mpl/int.hpp&gt;
61namespace mpl = boost::mpl;
62
63BOOST_STATIC_ASSERT((
64    mpl::plus&lt;
65        mpl::int_&lt;2&gt;
66      , mpl::int_&lt;3&gt;
67    &gt;::type::value == 5
68));
69</pre>
70<!-- @ compile(pop = None) -->
71<div class="sidebar">
72<p class="sidebar-title first"><tt class="literal"><span class="pre">BOOST_STATIC_ASSERT</span></tt></p>
73<p>is a macro that causes a compilation error if its argument is
74false.  The double parentheses are required because the C++
75preprocessor can't parse templates: it would otherwise be
76fooled by the comma into treating the condition as two separate
77macro arguments.  Unlike its runtime analogue <tt class="literal"><span class="pre">assert(...)</span></tt>,
78<tt class="literal"><span class="pre">BOOST_STATIC_ASSERT</span></tt> can also be used at class scope,
79allowing us to put assertions in our metafunctions.  See
80Chapter <a class="reference" href="./resources.html">8</a> for an in-depth discussion.</p>
81</div>
82<!-- @prefix.append('#include <boost/static_assert.hpp>') -->
83<p>At this point it might seem as though we have a solution, but we're
84not quite there yet.  A naive attempt to apply the <tt class="literal"><span class="pre">transform</span></tt>
85algorithm in the implementation of <tt class="literal"><span class="pre">operator*</span></tt> yields a compiler
86error:</p>
87<pre class="literal-block">
88#include &lt;boost/mpl/transform.hpp&gt;
89
90template &lt;class T, class D1, class D2&gt;
91quantity&lt; 
92    T
93  , typename mpl::transform&lt;D1,D2,mpl::plus&gt;::type
94&gt;
95operator*(quantity&lt;T,D1&gt; x, quantity&lt;T,D2&gt; y) { ... }
96</pre>
97<!-- @ example.replace('{ ... }',';')
98compile('all', pop = 1, expect_error = True)
99prefix +=['#include <boost/mpl/transform.hpp>'] -->
100<!-- @litre_translator.line_offset -= 7 -->
101<p>It fails because the protocol says that metafunction arguments
102must be types, and <tt class="literal"><span class="pre">plus</span></tt> is not a type, but a class template.
103Somehow we need to make metafunctions like <tt class="literal"><span class="pre">plus</span></tt> fit the
104metadata mold.</p>
105<p>One natural way to introduce polymorphism between metafunctions and
106metadata is to employ the wrapper idiom that gave us polymorphism
107between types and integral constants.  Instead of a nested integral
108constant, we can use a class template nested within a
109<strong>metafunction class</strong>:</p>
110<pre class="literal-block">
111struct plus_f
112{
113    template &lt;class T1, class T2&gt;
114    struct apply
115    {
116       typedef typename mpl::plus&lt;T1,T2&gt;::type type;
117    };
118};
119</pre>
120<div class="admonition-definition admonition">
121<p class="admonition-title first">Definition</p>
122<p>A <strong>Metafunction Class</strong> is a class with a publicly accessible
123nested metafunction called <tt class="literal"><span class="pre">apply</span></tt>.</p>
124</div>
125<p>Whereas a metafunction is a template but not a type, a
126metafunction class wraps that template within an ordinary
127non-templated class, which <em>is</em> a type.  Since metafunctions
128operate on and return types, a metafunction class can be passed as
129an argument to, or returned from, another metafunction.</p>
130<p>Finally, we have a <tt class="literal"><span class="pre">BinaryOperation</span></tt> type that we can pass to
131<tt class="literal"><span class="pre">transform</span></tt> without causing a compilation error:</p>
132<pre class="literal-block">
133template &lt;class T, class D1, class D2&gt;
134quantity&lt; 
135    T
136  , typename mpl::transform&lt;D1,D2,<strong>plus_f</strong>&gt;::type  // new dimensions
137&gt;
138operator*(quantity&lt;T,D1&gt; x, quantity&lt;T,D2&gt; y)
139{
140    typedef typename mpl::transform&lt;D1,D2,<strong>plus_f</strong>&gt;::type dim;
141    return quantity&lt;T,dim&gt;( x.value() * y.value() );
142}
143</pre>
144<p>Now, if we want to compute the force exterted by gravity on a 5 kilogram
145laptop computer, that's just the acceleration due to gravity (9.8
146m/sec<sup>2</sup>) times the mass of the laptop:</p>
147<pre class="literal-block">
148quantity&lt;float,mass&gt; m(5.0f);
149quantity&lt;float,acceleration&gt; a(9.8f);
150std::cout &lt;&lt; &quot;force = &quot; &lt;&lt; (m * a).value();
151</pre>
152<!-- @example.wrap('#include <iostream>\nvoid ff() {', '}')
153
154compile('all', pop = 1) -->
155<p>Our <tt class="literal"><span class="pre">operator*</span></tt> multiplies the runtime values (resulting in
1566.0f), and our metaprogram code uses <tt class="literal"><span class="pre">transform</span></tt> to sum the
157meta-sequences of fundamental dimension exponents, so that the
158result type contains a representation of a new list of exponents,
159something like:</p>
160<pre class="literal-block">
161mpl::vector_c&lt;int,1,1,-2,0,0,0,0&gt;
162</pre>
163<!-- @example.wrap('''
164    #include <boost/mpl/vector_c.hpp>
165    typedef''', 'xxxx;')
166compile() -->
167<!-- @litre_translator.line_offset -= 7 -->
168<p>However, if we try to write:</p>
169<pre class="literal-block">
170quantity&lt;float,force&gt; f = m * a;
171</pre>
172<!-- @ ma_function_args = '(quantity<float,mass> m, quantity<float,acceleration> a)'
173
174example.wrap('void bogus%s {' % ma_function_args, '}')
175compile('all', pop = 1, expect_error = True) -->
176<!-- @litre_translator.line_offset -= 7 -->
177<p>we'll run into a little problem.  Although the result of
178<tt class="literal"><span class="pre">m</span> <span class="pre">*</span> <span class="pre">a</span></tt> does indeed represent a force with exponents of mass,
179length, and time 1, 1, and -2 respectively, the type returned by
180<tt class="literal"><span class="pre">transform</span></tt> isn't a specialization of <tt class="literal"><span class="pre">vector_c</span></tt>.  Instead,
181<tt class="literal"><span class="pre">transform</span></tt> works generically on the elements of its inputs and
182builds a new sequence with the appropriate elements: a type with
183many of the same sequence properties as
184<tt class="literal"><span class="pre">mpl::vector_c&lt;int,1,1,-2,0,0,0,0&gt;</span></tt>, but with a different C++ type
185altogether.  If you want to see the type's full name, you can try
186to compile the example yourself and look at the error message, but
187the exact details aren't important.  The point is that
188<tt class="literal"><span class="pre">force</span></tt> names a different type, so the assignment above will fail.</p>
189<p>In order to resolve the problem, we can add an implicit conversion
190from the multiplication's result type to <tt class="literal"><span class="pre">quantity&lt;float,force&gt;</span></tt>.
191Since we can't predict the exact types of the dimensions involved
192in any computation, this conversion will have to be templated,
193something like:</p>
194<pre class="literal-block">
195template &lt;class T, class Dimensions&gt;
196struct quantity
197{
198    // converting constructor
199    template &lt;class OtherDimensions&gt;
200    quantity(quantity&lt;T,OtherDimensions&gt; const&amp; rhs)
201      : m_value(rhs.value())
202    {
203    }
204    ...
205</pre>
206<!-- @ example.append("""
207      explicit quantity(T x)
208         : m_value(x)
209      {}
210
211      T value() const { return m_value; }
212   private:
213      T m_value;
214  };""")
215
216stack[quantity_declaration] = example
217ignore()  -->
218<p>Unfortunately, such a general conversion undermines our whole
219purpose, allowing nonsense such as:</p>
220<pre class="literal-block">
221// Should yield a force, not a mass!
222quantity&lt;float,mass&gt; bogus = m * a;
223</pre>
224<!-- @ example.wrap('void bogus2%s {' % ma_function_args, '}')
225bogus_example = example
226compile('all', pop = 1) -->
227<p>We can correct that problem using another MPL algorithm,
228<tt class="literal"><span class="pre">equal</span></tt>, which tests that two sequences have the same elements:</p>
229<pre class="literal-block">
230template &lt;class OtherDimensions&gt;
231quantity(quantity&lt;T,OtherDimensions&gt; const&amp; rhs)
232  : m_value(rhs.value())
233{
234    BOOST_STATIC_ASSERT((
235       mpl::equal&lt;Dimensions,OtherDimensions&gt;::type::value
236    ));
237}
238</pre>
239<!-- @ example.wrap('''
240   #include <boost/mpl/equal.hpp>
241
242   template <class T, class Dimensions>
243   struct quantity
244   {
245       explicit quantity(T x)
246          : m_value(x)
247       {}
248
249       T value() const { return m_value; }
250   ''','''
251    private:
252       T m_value;
253   };''')
254
255stack[quantity_declaration] = example
256stack[-1] = bogus_example
257compile('all', pop = 1, expect_error = True) -->
258<p>Now, if the dimensions of the two quantities fail to match, the
259assertion will cause a compilation error.</p>
260</div>
261
262<div class="footer-separator"></div>
263<table class="footer"><tr class="footer"><td class="header-group navigation-bar"><span class="navigation-group"><a href="./implementing-addition-and.html" class="navigation-link">Prev</a>&nbsp;<a href="./implementing-division.html" class="navigation-link">Next</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./implementing-addition-and.html" class="navigation-link">Back</a>&nbsp;<a href="./implementing-division.html" class="navigation-link">Along</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./dimensional-analysis.html" class="navigation-link">Up</a>&nbsp;<a href="../index.html" class="navigation-link">Home</a></span><span class="navigation-group-separator">&nbsp;|&nbsp;</span><span class="navigation-group"><a href="./tutorial_toc.html" class="navigation-link">Full TOC</a></span></td>
264</tr></table></body>
265</html>
Note: See TracBrowser for help on using the repository browser.