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