1
0
forked from boostorg/mp11

Add another example

This commit is contained in:
Peter Dimov
2017-05-19 02:43:47 +03:00
parent 7d91a174a2
commit 93703f9c31
2 changed files with 155 additions and 1 deletions

View File

@@ -38,6 +38,8 @@
<dd><dl> <dd><dl>
<dt><span class="section"><a href="mp11.html#mp11.examples.generating_test_cases">Generating Test <dt><span class="section"><a href="mp11.html#mp11.examples.generating_test_cases">Generating Test
Cases</a></span></dt> Cases</a></span></dt>
<dt><span class="section"><a href="mp11.html#mp11.examples.writing_common_type_specializati">Writing
<code class="computeroutput"><span class="identifier">common_type</span></code> specializations</a></span></dt>
<dt><span class="section"><a href="mp11.html#mp11.examples.fixing_tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a></span></dt> <dt><span class="section"><a href="mp11.html#mp11.examples.fixing_tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a></span></dt>
<dt><span class="section"><a href="mp11.html#mp11.examples.computing_return_types">Computing Return <dt><span class="section"><a href="mp11.html#mp11.examples.computing_return_types">Computing Return
Types</a></span></dt> Types</a></span></dt>
@@ -401,6 +403,102 @@
</div> </div>
<div class="section"> <div class="section">
<div class="titlepage"><div><div><h3 class="title"> <div class="titlepage"><div><div><h3 class="title">
<a name="mp11.examples.writing_common_type_specializati"></a><a class="link" href="mp11.html#mp11.examples.writing_common_type_specializati" title="Writing common_type specializations">Writing
<code class="computeroutput"><span class="identifier">common_type</span></code> specializations</a>
</h3></div></div></div>
<p>
The standard trait <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>, used to obtain a type to which
all of its arguments can convert without unnecessary loss of precision, can
be user-specialized when its default implementation (based on the ternary
<code class="computeroutput"><span class="special">?:</span></code> operator) is unsuitable.
</p>
<p>
Let's write a <code class="computeroutput"><span class="identifier">common_type</span></code>
specialization for two <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span></code>
arguments. For that, we need a metafunction that applies <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>
to each pair of elements and gathers the results into a tuple:
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">Tp1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">Tp2</span><span class="special">&gt;</span> <span class="keyword">using</span> <span class="identifier">common_tuple</span> <span class="special">=</span> <span class="identifier">mp_transform</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special">,</span> <span class="identifier">Tp1</span><span class="special">,</span> <span class="identifier">Tp2</span><span class="special">&gt;;</span>
</pre>
<p>
then specialize <code class="computeroutput"><span class="identifier">common_type</span></code>
to use it:
</p>
<pre class="programlisting"><span class="keyword">namespace</span> <span class="identifier">std</span>
<span class="special">{</span>
<span class="keyword">template</span><span class="special">&lt;</span><span class="keyword">class</span><span class="special">...</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">T2</span><span class="special">&gt;</span> <span class="keyword">struct</span> <span class="identifier">common_type</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">...&gt;,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T2</span><span class="special">...&gt;&gt;:</span> <span class="identifier">mp_defer</span><span class="special">&lt;</span><span class="identifier">common_tuple</span><span class="special">,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">...&gt;,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T2</span><span class="special">...&gt;&gt;</span>
<span class="special">{</span>
<span class="special">};</span>
<span class="special">}</span> <span class="comment">// std</span>
</pre>
<p>
(There is no need to specialize <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type</span></code>
for more than two arguments - it takes care of synthesizing the appropriate
semantics from the binary case.)
</p>
<p>
The subtlety here is the use of <code class="computeroutput"><span class="identifier">mp_defer</span></code>.
We could have defined a nested <code class="computeroutput"><span class="identifier">type</span></code>
to <code class="computeroutput"><span class="identifier">common_tuple</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">...&gt;,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">tuple</span><span class="special">&lt;</span><span class="identifier">T2</span><span class="special">...&gt;&gt;</span></code>, and it would still have worked
in all valid cases. By letting <code class="computeroutput"><span class="identifier">mp_defer</span></code>
define <code class="computeroutput"><span class="identifier">type</span></code>, though, we make
our specialization <span class="emphasis"><em>SFINAE-friendly</em></span>.
</p>
<p>
That is, when our <code class="computeroutput"><span class="identifier">common_tuple</span></code>
causes a substitution failure instead of a hard error, <code class="computeroutput"><span class="identifier">mp_defer</span></code>
will not define a nested <code class="computeroutput"><span class="identifier">type</span></code>,
and <code class="computeroutput"><span class="identifier">common_type_t</span></code>, which
is defined as <code class="computeroutput"><span class="keyword">typename</span> <span class="identifier">common_type</span><span class="special">&lt;...&gt;::</span><span class="identifier">type</span></code>,
will also cause a substitution failure.
</p>
<p>
As another example, consider the hypothetical type <code class="computeroutput"><span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">T</span><span class="special">,</span>
<span class="identifier">E</span><span class="special">...&gt;</span></code>
that represents either a successful return with a value of <code class="computeroutput"><span class="identifier">T</span></code>, or an unsucessful return with an error
code of some type in the list <code class="computeroutput"><span class="identifier">E</span><span class="special">...</span></code>. The common type of <code class="computeroutput"><span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">,</span>
<span class="identifier">E2</span><span class="special">,</span> <span class="identifier">E3</span><span class="special">&gt;</span></code>
and <code class="computeroutput"><span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">T2</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E4</span><span class="special">,</span>
<span class="identifier">E5</span><span class="special">&gt;</span></code>
is <code class="computeroutput"><span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">&gt;,</span>
<span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">,</span> <span class="identifier">E3</span><span class="special">,</span> <span class="identifier">E4</span><span class="special">,</span>
<span class="identifier">E5</span><span class="special">&gt;</span></code>.
That is, the possible return values are combined into their common type,
and we take the union of the set of error types.
</p>
<p>
Therefore,
</p>
<pre class="programlisting"><span class="keyword">template</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">E1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T2</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">E2</span><span class="special">&gt;</span> <span class="keyword">using</span> <span class="identifier">common_expected</span> <span class="special">=</span> <span class="identifier">mp_rename</span><span class="special">&lt;</span><span class="identifier">mp_push_front</span><span class="special">&lt;</span><span class="identifier">mp_unique</span><span class="special">&lt;</span><span class="identifier">mp_append</span><span class="special">&lt;</span><span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">&gt;&gt;,</span> <span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">&gt;&gt;,</span> <span class="identifier">expected</span><span class="special">&gt;;</span>
<span class="keyword">namespace</span> <span class="identifier">std</span>
<span class="special">{</span>
<span class="keyword">template</span><span class="special">&lt;</span><span class="keyword">class</span> <span class="identifier">T1</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">E1</span><span class="special">,</span> <span class="keyword">class</span> <span class="identifier">T2</span><span class="special">,</span> <span class="keyword">class</span><span class="special">...</span> <span class="identifier">E2</span><span class="special">&gt;</span> <span class="keyword">struct</span> <span class="identifier">common_type</span><span class="special">&lt;</span><span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">E1</span><span class="special">...&gt;,</span> <span class="identifier">expected</span><span class="special">&lt;</span><span class="identifier">T2</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">...&gt;&gt;:</span> <span class="identifier">mp_defer</span><span class="special">&lt;</span><span class="identifier">common_expected</span><span class="special">,</span> <span class="identifier">T1</span><span class="special">,</span> <span class="identifier">mp_list</span><span class="special">&lt;</span><span class="identifier">E1</span><span class="special">...&gt;,</span> <span class="identifier">T2</span><span class="special">,</span> <span class="identifier">mp_list</span><span class="special">&lt;</span><span class="identifier">E2</span><span class="special">...&gt;&gt;</span>
<span class="special">{</span>
<span class="special">};</span>
<span class="special">}</span> <span class="comment">// std</span>
</pre>
<p>
Here we've taken a different tack; instead of passing the <code class="computeroutput"><span class="identifier">expected</span></code>
types to <code class="computeroutput"><span class="identifier">common_expected</span></code>,
we're passing the <code class="computeroutput"><span class="identifier">T</span></code> types
and lists of the <code class="computeroutput"><span class="identifier">E</span></code> types.
This makes our job easier. <code class="computeroutput"><span class="identifier">mp_unique</span><span class="special">&lt;</span><span class="identifier">mp_append</span><span class="special">&lt;</span><span class="identifier">E1</span><span class="special">,</span> <span class="identifier">E2</span><span class="special">&gt;&gt;</span></code>
gives us the concatenation of <code class="computeroutput"><span class="identifier">E1</span></code>
and <code class="computeroutput"><span class="identifier">E2</span></code> with the duplicates
removed; we then add <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">common_type_t</span><span class="special">&lt;</span><span class="identifier">T1</span><span class="special">,</span> <span class="identifier">T2</span><span class="special">&gt;</span></code>
to the front via <code class="computeroutput"><span class="identifier">mp_push_front</span></code>;
and finally, we <code class="computeroutput"><span class="identifier">mp_rename</span></code>
the resultant <code class="computeroutput"><span class="identifier">mp_list</span></code> to
<code class="computeroutput"><span class="identifier">expected</span></code>.
</p>
</div>
<div class="section">
<div class="titlepage"><div><div><h3 class="title">
<a name="mp11.examples.fixing_tuple_cat"></a><a class="link" href="mp11.html#mp11.examples.fixing_tuple_cat" title="Fixing tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a> <a name="mp11.examples.fixing_tuple_cat"></a><a class="link" href="mp11.html#mp11.examples.fixing_tuple_cat" title="Fixing tuple_cat">Fixing <code class="computeroutput"><span class="identifier">tuple_cat</span></code></a>
</h3></div></div></div> </h3></div></div></div>
<p> <p>
@@ -2317,7 +2415,7 @@
</div> </div>
</div> </div>
<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr> <table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
<td align="left"><p><small>Last revised: May 18, 2017 at 22:41:31 GMT</small></p></td> <td align="left"><p><small>Last revised: May 18, 2017 at 23:36:45 GMT</small></p></td>
<td align="right"><div class="copyright-footer"></div></td> <td align="right"><div class="copyright-footer"></div></td>
</tr></table> </tr></table>
<hr> <hr>

View File

@@ -87,6 +87,62 @@ tuple element; we use a (C++14) lambda that calls `test_result`. (In pure C++11,
function object with a templated `operator()` and pass that to `tuple_for_each` directly.) function object with a templated `operator()` and pass that to `tuple_for_each` directly.)
[endsect] [endsect]
[section Writing `common_type` specializations]
The standard trait `std::common_type`, used to obtain a type to which all of its arguments can convert without
unnecessary loss of precision, can be user-specialized when its default implementation (based on the ternary `?:`
operator) is unsuitable.
Let's write a `common_type` specialization for two `std::tuple` arguments. For that, we need a metafunction that
applies `std::common_type` to each pair of elements and gathers the results into a tuple:
template<class Tp1, class Tp2> using common_tuple = mp_transform<std::common_type_t, Tp1, Tp2>;
then specialize `common_type` to use it:
namespace std
{
template<class... T1, class... T2> struct common_type<std::tuple<T1...>, std::tuple<T2...>>: mp_defer<common_tuple, std::tuple<T1...>, std::tuple<T2...>>
{
};
} // std
(There is no need to specialize `std::common_type` for more than two arguments - it takes care of synthesizing the appropriate semantics from
the binary case.)
The subtlety here is the use of `mp_defer`. We could have defined a nested `type` to `common_tuple<std::tuple<T1...>, std::tuple<T2...>>`,
and it would still have worked in all valid cases. By letting `mp_defer` define `type`, though, we make our specialization /SFINAE-friendly/.
That is, when our `common_tuple` causes a substitution failure instead of a hard error, `mp_defer` will not define a nested `type`,
and `common_type_t`, which is defined as `typename common_type<...>::type`, will also cause a substitution failure.
As another example, consider the hypothetical type `expected<T, E...>` that represents either a successful return with a value of `T`,
or an unsucessful return with an error code of some type in the list `E...`. The common type of `expected<T1, E1, E2, E3>` and
`expected<T2, E1, E4, E5>` is `expected<std::common_type_t<T1, T2>, E1, E2, E3, E4, E5>`. That is, the possible return values are
combined into their common type, and we take the union of the set of error types.
Therefore,
template<class T1, class E1, class T2, class E2> using common_expected = mp_rename<mp_push_front<mp_unique<mp_append<E1, E2>>, std::common_type_t<T1, T2>>, expected>;
namespace std
{
template<class T1, class... E1, class T2, class... E2> struct common_type<expected<T1, E1...>, expected<T2, E2...>>: mp_defer<common_expected, T1, mp_list<E1...>, T2, mp_list<E2...>>
{
};
} // std
Here we've taken a different tack; instead of passing the `expected` types to `common_expected`, we're passing the `T` types and lists of
the `E` types. This makes our job easier. `mp_unique<mp_append<E1, E2>>` gives us the concatenation of `E1` and `E2` with the duplicates
removed; we then add `std::common_type_t<T1, T2>` to the front via `mp_push_front`; and finally, we `mp_rename` the resultant `mp_list`
to `expected`.
[endsect]
[section Fixing `tuple_cat`] [section Fixing `tuple_cat`]
The article [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html Simple C++11 metaprogramming] builds an The article [@http://pdimov.com/cpp2/simple_cxx11_metaprogramming.html Simple C++11 metaprogramming] builds an