• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<html>
2<head>
3<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
4<title>Rationale</title>
5<link rel="stylesheet" href="../boostbook.css" type="text/css">
6<meta name="generator" content="DocBook XSL Stylesheets V1.79.1">
7<link rel="home" href="../index.html" title="Chapter 1. Boost.Bimap">
8<link rel="up" href="../index.html" title="Chapter 1. Boost.Bimap">
9<link rel="prev" href="release_notes.html" title="Release notes">
10<link rel="next" href="rationale/additional_features.html" title="Additional Features">
11</head>
12<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
13<table cellpadding="2" width="100%"><tr>
14<td valign="top"><img alt="Boost C++ Libraries" width="277" height="86" src="../../../../../boost.png"></td>
15<td align="center"><a href="../../../../../index.html">Home</a></td>
16<td align="center"><a href="../../../../../libs/libraries.htm">Libraries</a></td>
17<td align="center"><a href="http://www.boost.org/users/people.html">People</a></td>
18<td align="center"><a href="http://www.boost.org/users/faq.html">FAQ</a></td>
19<td align="center"><a href="../../../../../more/index.htm">More</a></td>
20</tr></table>
21<hr>
22<div class="spirit-nav">
23<a accesskey="p" href="release_notes.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="rationale/additional_features.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
24</div>
25<div class="section">
26<div class="titlepage"><div><div><h2 class="title" style="clear: both">
27<a name="boost_bimap.rationale"></a><a class="link" href="rationale.html" title="Rationale">Rationale</a>
28</h2></div></div></div>
29<div class="toc"><dl class="toc">
30<dt><span class="section"><a href="rationale.html#boost_bimap.rationale.general_design">General Design</a></span></dt>
31<dt><span class="section"><a href="rationale/additional_features.html">Additional
32      Features</a></span></dt>
33<dt><span class="section"><a href="rationale/code.html">Code</a></span></dt>
34<dt><span class="section"><a href="rationale/the_student_and_the_mentor.html">The
35      student and the mentor</a></span></dt>
36</dl></div>
37<p>
38      This section assumes that you have read all other sections, the most of important
39      of which being <span class="emphasis"><em>tutorial</em></span>, <span class="emphasis"><em>std::set theory</em></span>
40      and the <span class="emphasis"><em>reference</em></span>, and that you have tested the library.
41      A lot of effort was invested in making the interface as intuitive and clean
42      as possible. If you understand, and hopefully like, the interface of this library,
43      it will be a lot easier to read this rationale. The following section is little
44      more than a rationale. This library was coded in the context of the Google
45      SoC 2006 and the student and mentor were in different continents. A great deal
46      of email flowed between Joaquin and Matias. The juiciest parts of the conversations
47      where extracted and rearranged here.
48    </p>
49<div class="note"><table border="0" summary="Note">
50<tr>
51<td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="../../../../../doc/src/images/note.png"></td>
52<th align="left">Note</th>
53</tr>
54<tr><td align="left" valign="top"><p>
55        To browse the code, you can use the <a href="../doxydoc/index.html" target="_top"><span class="emphasis"><em>Bimap
56        Complete Reference</em></span></a>, a doxygen-powered document targeted
57        at developers.
58      </p></td></tr>
59</table></div>
60<div class="section">
61<div class="titlepage"><div><div><h3 class="title">
62<a name="boost_bimap.rationale.general_design"></a><a class="link" href="rationale.html#boost_bimap.rationale.general_design" title="General Design">General Design</a>
63</h3></div></div></div>
64<p>
65        The initial explanation includes few features. This section aims to describe
66        the general design of the library and excludes details of those features
67        that are of lesser importance; these features will be introduced later.
68      </p>
69<p>
70        The design of the library is divided into two parts. The first is the construction
71        of a <code class="literal">relation</code> class. This will be the object stored and
72        managed by the <code class="literal">multi_index_container</code> core. The idea is
73        to make this class as easy as possible to use, while making it efficient
74        in terms of memory and access time. This is a cornerstone in the design of
75        <span class="bold"><strong>Boost.Bimap</strong></span> and, as you will see in this
76        rationale, the rest of the design follows easily.
77      </p>
78<p>
79        The following interface is necessary for the <code class="literal">relation</code>
80        class:
81      </p>
82<pre class="programlisting"><span class="keyword">typedef</span> <span class="special">-</span><span class="identifier">unspecified</span><span class="special">-</span> <span class="identifier">TA</span><span class="special">;</span> <span class="keyword">typedef</span> <span class="special">-</span><span class="identifier">unspecified</span><span class="special">-</span> <span class="identifier">TB</span><span class="special">;</span>
83
84<span class="identifier">TA</span> <span class="identifier">a</span><span class="special">,</span> <span class="identifier">ai</span><span class="special">;</span> <span class="identifier">TB</span> <span class="identifier">b</span><span class="special">,</span> <span class="identifier">bi</span><span class="special">;</span>
85
86<span class="keyword">typedef</span> <span class="identifier">relation</span><span class="special">&lt;</span> <span class="identifier">TA</span><span class="special">,</span> <span class="identifier">TB</span> <span class="special">&gt;</span> <span class="identifier">rel</span><span class="special">;</span>
87<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">rel</span><span class="special">::</span><span class="identifier">left_type</span> <span class="special">,</span> <span class="identifier">TA</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
88<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">rel</span><span class="special">::</span><span class="identifier">right_type</span><span class="special">,</span> <span class="identifier">TB</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
89
90<span class="identifier">rel</span> <span class="identifier">r</span><span class="special">(</span><span class="identifier">ai</span><span class="special">,</span><span class="identifier">bi</span><span class="special">);</span>
91<span class="identifier">assert</span><span class="special">(</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span> <span class="special">==</span> <span class="identifier">ai</span> <span class="special">&amp;&amp;</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">right</span> <span class="special">==</span> <span class="identifier">bi</span> <span class="special">);</span>
92
93<span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span>  <span class="special">=</span> <span class="identifier">a</span><span class="special">;</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">right</span> <span class="special">=</span> <span class="identifier">b</span><span class="special">;</span>
94<span class="identifier">assert</span><span class="special">(</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span>  <span class="special">==</span> <span class="identifier">a</span> <span class="special">&amp;&amp;</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">right</span> <span class="special">==</span> <span class="identifier">b</span> <span class="special">);</span>
95
96<span class="keyword">typedef</span> <span class="identifier">pair_type_by</span><span class="special">&lt;</span> <span class="identifier">member_at</span><span class="special">::</span><span class="identifier">left</span> <span class="special">,</span> <span class="identifier">rel</span> <span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">pba_type</span><span class="special">;</span>
97<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">pba_type</span><span class="special">::</span><span class="identifier">first_type</span> <span class="special">,</span> <span class="identifier">TA</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
98<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">pba_type</span><span class="special">::</span><span class="identifier">second_type</span><span class="special">,</span> <span class="identifier">TB</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
99
100<span class="keyword">typedef</span> <span class="identifier">pair_type_by</span><span class="special">&lt;</span> <span class="identifier">member_at</span><span class="special">::</span><span class="identifier">right</span><span class="special">,</span> <span class="identifier">rel</span> <span class="special">&gt;::</span><span class="identifier">type</span> <span class="identifier">pbb_type</span><span class="special">;</span>
101<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">pbb_type</span><span class="special">::</span><span class="identifier">first_type</span> <span class="special">,</span> <span class="identifier">TB</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
102<span class="identifier">STATIC_ASSERT</span><span class="special">(</span> <span class="identifier">is_same</span><span class="special">&lt;</span> <span class="identifier">pbb_type</span><span class="special">::</span><span class="identifier">second_type</span><span class="special">,</span> <span class="identifier">TA</span> <span class="special">&gt;::</span><span class="identifier">value</span> <span class="special">);</span>
103
104<span class="identifier">pba_type</span> <span class="identifier">pba</span> <span class="special">=</span> <span class="identifier">pair_by</span><span class="special">&lt;</span> <span class="identifier">member_at</span><span class="special">::</span><span class="identifier">left</span>  <span class="special">&gt;(</span><span class="identifier">r</span><span class="special">);</span>
105<span class="identifier">assert</span><span class="special">(</span> <span class="identifier">pba</span><span class="special">.</span><span class="identifier">first</span> <span class="special">==</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span>  <span class="special">&amp;&amp;</span> <span class="identifier">pba</span><span class="special">.</span><span class="identifier">second</span> <span class="special">==</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">right</span> <span class="special">);</span>
106
107<span class="identifier">pbb_type</span> <span class="identifier">pbb</span> <span class="special">=</span> <span class="identifier">pair_by</span><span class="special">&lt;</span> <span class="identifier">member_at</span><span class="special">::</span><span class="identifier">right</span> <span class="special">&gt;(</span><span class="identifier">r</span><span class="special">);</span>
108<span class="identifier">assert</span><span class="special">(</span> <span class="identifier">pbb</span><span class="special">.</span><span class="identifier">first</span> <span class="special">==</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">right</span> <span class="special">&amp;&amp;</span> <span class="identifier">pbb</span><span class="special">.</span><span class="identifier">second</span> <span class="special">==</span> <span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span>  <span class="special">);</span>
109</pre>
110<p>
111        <span class="inlinemediaobject"><img src="../images/bimap/relation.png" alt="relation"></span>
112      </p>
113<p>
114        Although this seems straightforward, as will be seen later, it is the most
115        difficult code hack of the library. It is indeed very easy if we relax some
116        of the efficiency constraints. For example, it is trivial if we allow a relation
117        to have greater size than the the sum of those of its components. It is equally
118        simple if access speed is not important. One of the first decisions made
119        about <span class="bold"><strong>Boost.Bimap</strong></span> was, however, that, in
120        order to be useful, it had to achieve zero overhead over the wrapped <span class="bold"><strong>Boost.MultiIndex</strong></span> container. Finally, there is another
121        constraint that can be relaxed: conformance to C++ standards, but this is
122        quite unacceptable. Let us now suppose that we have coded this class, and
123        it conforms to what was required.
124      </p>
125<p>
126        The second part is based on this <code class="literal">relation</code> class. We can
127        now view the data in any of three ways: <code class="computeroutput"><span class="identifier">pair</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span></code>,
128        <code class="computeroutput"><span class="identifier">relation</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span></code> and <code class="computeroutput"><span class="identifier">pair</span><span class="special">&lt;</span><span class="identifier">B</span><span class="special">,</span><span class="identifier">A</span><span class="special">&gt;</span></code>.
129        Suppose that our bimap supports only one-to-one relations. (Other relation
130        types are considered additional features in this design.) The proposed interface
131        is very simple, and it is based heavily on the concepts of the STL. Given
132        a <code class="computeroutput"><span class="identifier">bimap</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span> <span class="identifier">bm</span></code>:
133      </p>
134<div class="orderedlist"><ol class="orderedlist" type="1">
135<li class="listitem">
136            <code class="computeroutput"><span class="identifier">bm</span><span class="special">.</span><span class="identifier">left</span></code> is signature-compatible with a
137            <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">map</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span></code>
138          </li>
139<li class="listitem">
140            <code class="computeroutput"><span class="identifier">bm</span><span class="special">.</span><span class="identifier">right</span></code> is signature-compatible with
141            a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">map</span><span class="special">&lt;</span><span class="identifier">B</span><span class="special">,</span><span class="identifier">A</span><span class="special">&gt;</span></code>
142          </li>
143<li class="listitem">
144            <code class="computeroutput"><span class="identifier">bm</span></code> is signature-compatible
145            with a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">set</span><span class="special">&lt;</span><span class="identifier">relation</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span> <span class="special">&gt;</span></code>
146          </li>
147</ol></div>
148<p>
149        <span class="inlinemediaobject"><img src="../images/bimap/simple.bimap.png" alt="simple.bimap"></span>
150      </p>
151<p>
152        This interface is easily learned by users who have a STL background, as well
153        as being simple and powerful. This is the general design.
154      </p>
155<h5>
156<a name="boost_bimap.rationale.general_design.h0"></a>
157        <span class="phrase"><a name="boost_bimap.rationale.general_design.relation_implementation"></a></span><a class="link" href="rationale.html#boost_bimap.rationale.general_design.relation_implementation">Relation
158        Implementation</a>
159      </h5>
160<p>
161        This section explains the details of the actual <code class="literal">relation</code>
162        class implementation.
163      </p>
164<p>
165        The first thing that we can imagine is the use of an <code class="literal">union</code>.
166        Regrettably, the current C++ standard only allows unions of POD types. For
167        the views, we can try a wrapper around a <code class="computeroutput"><span class="identifier">relation</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span></code> that
168        has two references named first and second that bind to <code class="computeroutput"><span class="identifier">A</span></code>
169        and <code class="computeroutput"><span class="identifier">B</span></code>, or to <code class="computeroutput"><span class="identifier">B</span></code> and <code class="computeroutput"><span class="identifier">A</span></code>.
170      </p>
171<pre class="programlisting"><span class="identifier">relation</span><span class="special">&lt;</span><span class="identifier">TA</span><span class="special">,</span><span class="identifier">TB</span><span class="special">&gt;</span> <span class="identifier">r</span><span class="special">;</span>
172
173<span class="identifier">const_reference_pair</span><span class="special">&lt;</span><span class="identifier">A</span><span class="special">,</span><span class="identifier">B</span><span class="special">&gt;</span> <span class="identifier">pba</span><span class="special">(</span><span class="identifier">r</span><span class="special">);</span>
174<span class="identifier">const_reference_pair</span><span class="special">&lt;</span><span class="identifier">B</span><span class="special">,</span><span class="identifier">A</span><span class="special">&gt;</span> <span class="identifier">pbb</span><span class="special">(</span><span class="identifier">r</span><span class="special">);</span>
175</pre>
176<p>
177        It is not difficult to code the relation class using this, but two references
178        are initialized at every access and using of <code class="computeroutput"><span class="identifier">pba</span><span class="special">.</span><span class="identifier">first</span></code>
179        will be slower in most compilers than using <code class="computeroutput"><span class="identifier">r</span><span class="special">.</span><span class="identifier">left</span></code> directly
180        . There is another hidden drawback of using this scheme: it is not iterator-friendly,
181        since the map views iterators must be degraded to <span class="emphasis"><em>Read Write</em></span>
182        instead of <span class="emphasis"><em>LValue</em></span>. This will be explained later.
183      </p>
184<p>
185        At first, this seems to be the best we can do with the current C++ standard.
186        However there is a solution to this problem that does not conform very well
187        to C++ standards but does achieve zero overhead in terms of access time and
188        memory, and additionally allows the view iterators to be upgraded to <span class="emphasis"><em>LValue</em></span>
189        again.
190      </p>
191<p>
192        In order to use this, the compiler must conform to a layout-compatibility
193        clause that is not currently in the standard but is very natural. The additional
194        clause imposes that if we have two classes:
195      </p>
196<pre class="programlisting"><span class="keyword">struct</span> <span class="identifier">class_a_b</span>
197<span class="special">{</span>
198    <span class="identifier">Type1</span> <span class="identifier">name_a</span><span class="special">;</span>
199    <span class="identifier">Type2</span> <span class="identifier">name_b</span><span class="special">;</span>
200<span class="special">};</span>
201
202<span class="keyword">struct</span> <span class="identifier">class_b_a</span>
203<span class="special">{</span>
204    <span class="identifier">Type1</span> <span class="identifier">name_b</span><span class="special">;</span>
205    <span class="identifier">Type2</span> <span class="identifier">name_a</span><span class="special">;</span>
206<span class="special">};</span>
207</pre>
208<p>
209        then the storage layout of <code class="literal">class_a_b</code> is equal to the storage
210        layout of <code class="literal">class_b_a</code>. If you are surprised to learn that
211        this does not hold in a standards-compliant C++ compiler, welcome to the
212        club. It is the natural way to implement it from the point of view of the
213        compiler's vendor and is very useful for the developer. Maybe it will be
214        included in the standard some day. Every current compiler conforms to this.
215      </p>
216<p>
217        If we are able to count on this, then we can implement an idiom called <code class="literal">mutant</code>.
218        The idea is to provide a secure wrapper around <code class="literal">reinterpret_cast</code>.
219        A class can declare that it can be viewed using different view classes that
220        are storage-compatible with it. Then we use the free function <code class="literal">mutate&lt;view&gt;(mutant)</code>
221        to get the view. The <code class="computeroutput"><span class="identifier">mutate</span></code>
222        function checks at compile time that the requested view is declared in the
223        mutant views list. We implement a class name <code class="computeroutput"><span class="identifier">structured_pair</span></code>
224        that is signature-compatible with a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">pair</span></code>,
225        while the storage layout is configured with a third template parameter. Two
226        instances of this template class will provide the views of the relation.
227      </p>
228<p>
229        The thing is that if we want to be standards-compliant, we cannot use this
230        approach. It is very annoying not to be able to use something that we know
231        will work with every compiler and that is far better than alternatives. So
232        -- and this is an important decision -- we have to find a way to use it and
233        still make the library standards-compliant.
234      </p>
235<p>
236        The idea is very simple. We code both approaches: the const_reference_pair-based
237        and the mutant-based, and use the mutant approach if the compiler is compliant
238        with our new layout-compatible clause. If the compiler really messes things
239        up, we degrade the performance of the bimap a little. The only drawback here
240        is that, while the mutant approach allows to make <span class="emphasis"><em>LValue</em></span>
241        iterators, we have to degrade them to <span class="emphasis"><em>Read Write</em></span> in
242        both cases, because we require that the same code be compilable by any standards-compliant
243        compiler.
244      </p>
245<div class="note"><table border="0" summary="Note">
246<tr>
247<td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="../../../../../doc/src/images/note.png"></td>
248<th align="left">Note</th>
249</tr>
250<tr><td align="left" valign="top"><p>
251          Testing this approach in all the supported compilers indicated that the
252          mutant idiom was always supported. The strictly compliant version was removed
253          from the code because it was never used.
254        </p></td></tr>
255</table></div>
256<h5>
257<a name="boost_bimap.rationale.general_design.h1"></a>
258        <span class="phrase"><a name="boost_bimap.rationale.general_design.bimap_implementation"></a></span><a class="link" href="rationale.html#boost_bimap.rationale.general_design.bimap_implementation">Bimap
259        Implementation</a>
260      </h5>
261<p>
262        The core of bimap will be obviously a <code class="computeroutput"><span class="identifier">multi_index_container</span></code>.
263        The basic idea to tackle the implementation of the bimap class is to use
264        <code class="literal">iterator_adaptor</code> to convert the iterators from Boost.MultiIndex
265        to the <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">map</span></code> and <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">set</span></code> behaviour.
266        The <code class="computeroutput"><span class="identifier">map_view</span></code> and the <code class="computeroutput"><span class="identifier">set_view</span></code> can be implemented directly using
267        this new transformed iterators and a wrapper around each index of the core
268        container. However, there is a hidden idiom here, that, once coded, will
269        be very useful for other parts of this library and for Boost.MRU library.
270        Following the ideas from <code class="computeroutput"><span class="identifier">iterator_adaptor</span></code>,
271        Boost.Bimap views are implemented using a <code class="literal">container_adaptor</code>.
272        There are several template classes (for example <code class="computeroutput"><span class="identifier">map_adaptor</span></code>
273        and <code class="computeroutput"><span class="identifier">set_adaptor</span></code>) that take
274        a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">map</span></code> signature-conformant class and new
275        iterators, and adapt the container so it now uses this iterators instead
276        of the originals. For example, if you have a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">set</span><span class="special">&lt;</span><span class="keyword">int</span><span class="special">*&gt;</span></code>,
277        you can build other container that behaves exactly as a <code class="computeroutput"><span class="identifier">std</span><span class="special">::</span><span class="identifier">set</span><span class="special">&lt;</span><span class="keyword">int</span><span class="special">&gt;</span></code> using
278        <code class="computeroutput"><span class="identifier">set_adaptor</span></code> and <code class="literal">iterator_adaptor</code>.
279        The combined use of this two tools is very powerful. A <code class="literal">container_adaptor</code>
280        can take classes that do not fulfill all the requirements of the adapted
281        container. The new container must define these missing functions.
282      </p>
283</div>
284</div>
285<table xmlns:rev="http://www.cs.rpi.edu/~gregod/boost/tools/doc/revision" width="100%"><tr>
286<td align="left"></td>
287<td align="right"><div class="copyright-footer">Copyright © 2006-2012 Matias Capeletto<p>
288        Distributed under the Boost Software License, Version 1.0. (See accompanying
289        file LICENSE_1_0.txt or copy at <a href="http://www.boost.org/LICENSE_1_0.txt" target="_top">http://www.boost.org/LICENSE_1_0.txt</a>)
290      </p>
291</div></td>
292</tr></table>
293<hr>
294<div class="spirit-nav">
295<a accesskey="p" href="release_notes.html"><img src="../../../../../doc/src/images/prev.png" alt="Prev"></a><a accesskey="u" href="../index.html"><img src="../../../../../doc/src/images/up.png" alt="Up"></a><a accesskey="h" href="../index.html"><img src="../../../../../doc/src/images/home.png" alt="Home"></a><a accesskey="n" href="rationale/additional_features.html"><img src="../../../../../doc/src/images/next.png" alt="Next"></a>
296</div>
297</body>
298</html>
299