implementing.html 14 KB

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