Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/boost_1_34_1/libs/variant/doc/design.xml @ 29

Last change on this file since 29 was 29, checked in by landauf, 16 years ago

updated boost from 1_33_1 to 1_34_1

File size: 16.3 KB
Line 
1<?xml version="1.0" encoding="utf-8"?>
2<!DOCTYPE section PUBLIC "-//Boost//DTD BoostBook XML V1.0//EN"
3  "http://www.boost.org/tools/boostbook/dtd/boostbook.dtd">
4<section id="variant.design">
5  <title>Design Overview</title>
6
7  <using-namespace name="boost"/>
8
9  <section id="variant.design.never-empty">
10    <title>&quot;Never-Empty&quot; Guarantee</title>
11
12    <section id="variant.design.never-empty.guarantee">
13      <title>The Guarantee</title>
14
15      <para>All instances <code>v</code> of type
16        <code><classname>variant</classname>&lt;T1,T2,...,TN&gt;</code>
17        guarantee that <code>v</code> has constructed content of one of the
18        types <code>T<emphasis>i</emphasis></code>, even if an operation on
19        <code>v</code> has previously failed.</para>
20
21      <para>This implies that <code>variant</code> may be viewed precisely as
22        a union of <emphasis>exactly</emphasis> its bounded types. This
23        &quot;never-empty&quot; property insulates the user from the
24        possibility of undefined <code>variant</code> content and the
25        significant additional complexity-of-use attendant with such a
26        possibility.</para>
27    </section>
28
29    <section id="variant.design.never-empty.problem">
30      <title>The Implementation Problem</title>
31
32      <para>While the
33        <link linkend="variant.design.never-empty.guarantee">never-empty guarantee</link>
34        might at first seem &quot;obvious,&quot; it is in fact not even
35        straightforward how to implement it in general (i.e., without
36        unreasonably restrictive additional requirements on
37        <link linkend="variant.concepts.bounded-type">bounded types</link>).</para>
38
39      <para>The central difficulty emerges in the details of
40        <code>variant</code> assignment. Given two instances <code>v1</code>
41        and <code>v2</code> of some concrete <code>variant</code> type, there
42        are two distinct, fundamental cases we must consider for the assignment
43        <code>v1 = v2</code>.</para>
44
45      <para>First consider the case that <code>v1</code> and <code>v2</code>
46        each contains a value of the same type. Call this type <code>T</code>.
47        In this situation, assignment is perfectly straightforward: use
48        <code>T::operator=</code>.</para>
49
50      <para>However, we must also consider the case that <code>v1</code> and
51        <code>v2</code> contain values <emphasis>of distinct types</emphasis>.
52        Call these types <code>T</code> and <code>U</code>. At this point,
53        since <code>variant</code> manages its content on the stack, the
54        left-hand side of the assignment (i.e., <code>v1</code>) must destroy
55        its content so as to permit in-place copy-construction of the content
56        of the right-hand side (i.e., <code>v2</code>). In the end, whereas
57        <code>v1</code> began with content of type <code>T</code>, it ends
58        with content of type <code>U</code>, namely a copy of the content of
59        <code>v2</code>.</para>
60
61      <para>The crux of the problem, then, is this: in the event that
62        copy-construction of the content of <code>v2</code> fails, how can
63        <code>v1</code> maintain its &quot;never-empty&quot; guarantee?
64        By the time copy-construction from <code>v2</code> is attempted,
65        <code>v1</code> has already destroyed its content!</para>
66    </section>
67
68    <section id="variant.design.never-empty.memcpy-solution">
69      <title>The &quot;Ideal&quot; Solution: False Hopes</title>
70
71      <para>Upon learning of this dilemma, clever individuals may propose the
72        following scheme hoping to solve the problem:
73
74        <orderedlist>
75          <listitem>Provide some &quot;backup&quot; storage, appropriately
76            aligned, capable of holding values of the contained type of the
77            left-hand side.</listitem>
78          <listitem>Copy the memory (e.g., using <code>memcpy</code>) of the
79            storage of the left-hand side to the backup storage.</listitem>
80          <listitem>Attempt a copy of the right-hand side content to the
81            (now-replicated) left-hand side storage.</listitem>
82          <listitem>In the event of an exception from the copy, restore the
83            backup (i.e., copy the memory from the backup storage back into
84            the left-hand side storage).</listitem>
85          <listitem>Otherwise, in the event of success, now copy the memory
86            of the left-hand side storage to another &quot;temporary&quot;
87            aligned storage.</listitem>
88          <listitem>Now restore the backup (i.e., again copying the memory)
89            to the left-hand side storage; with the &quot;old&quot; content
90            now restored, invoke the destructor of the contained type on the
91            storage of the left-hand side.</listitem>
92          <listitem>Finally, copy the memory of the temporary storage to the
93            (now-empty) storage of the left-hand side.</listitem>
94        </orderedlist>
95      </para>
96
97      <para>While complicated, it appears such a scheme could provide the
98        desired safety in a relatively efficient manner. In fact, several
99        early iterations of the library implemented this very approach.</para>
100
101      <para>Unfortunately, as Dave Abraham's first noted, the scheme results
102        in undefined behavior:
103
104        <blockquote>
105          <para>&quot;That's a lot of code to read through, but if it's
106            doing what I think it's doing, it's undefined behavior.</para>
107          <para>&quot;Is the trick to move the bits for an existing object
108            into a buffer so we can tentatively construct a new object in
109            that memory, and later move the old bits back temporarily to
110            destroy the old object?</para>
111          <para>&quot;The standard does not give license to do that: only one
112            object may have a given address at a time. See 3.8, and
113            particularly paragraph 4.&quot;</para>
114        </blockquote>
115      </para>
116
117      <para>Additionally, as close examination quickly reveals, the scheme has
118        the potential to create irreconcilable race-conditions in concurrent
119        environments.</para>
120
121      <para>Ultimately, even if the above scheme could be made to work on
122        certain platforms with particular compilers, it is still necessary to
123        find a portable solution.</para>
124    </section>
125
126    <section id="variant.design.never-empty.double-storage-solution">
127      <title>An Initial Solution: Double Storage</title>
128
129      <para>Upon learning of the infeasibility of the above scheme, Anthony
130        Williams proposed in
131        <link linkend="variant.refs.wil02">[Wil02]</link> a scheme that served
132        as the basis for a portable solution in some pre-release
133        implementations of <code>variant</code>.</para>
134
135      <para>The essential idea to this scheme, which shall be referred to as
136        the &quot;double storage&quot; scheme, is to provide enough space
137        within a <code>variant</code> to hold two separate values of any of
138        the bounded types.</para>
139
140      <para>With the secondary storage, a copy the right-hand side can be
141        attempted without first destroying the content of the left-hand side;
142        accordingly, the content of the left-hand side remains available in
143        the event of an exception.</para>
144
145      <para>Thus, with this scheme, the <code>variant</code> implementation
146        needs only to keep track of which storage contains the content -- and
147        dispatch any visitation requests, queries, etc. accordingly.</para>
148
149      <para>The most obvious flaw to this approach is the space overhead
150        incurred. Though some optimizations could be applied in special cases
151        to eliminate the need for double storage -- for certain bounded types
152        or in some cases entirely (see
153        <xref linkend="variant.design.never-empty.optimizations"/> for more
154        details) -- many users on the Boost mailing list strongly objected to
155        the use of double storage. In particular, it was noted that the
156        overhead of double storage would be at play at all times -- even if
157        assignment to <code>variant</code> never occurred. For this reason
158        and others, a new approach was developed.</para>
159    </section>
160
161    <section id="variant.design.never-empty.heap-backup-solution">
162      <title>Current Approach: Temporary Heap Backup</title>
163
164      <para>Despite the many objections to the double storage solution, it was
165        realized that no replacement would be without drawbacks. Thus, a
166        compromise was desired.</para>
167
168      <para>To this end, Dave Abrahams suggested to include the following in
169        the behavior specification for <code>variant</code> assignment:
170        &quot;<code>variant</code> assignment from one type to another may
171        incur dynamic allocation." That is, while <code>variant</code> would
172        continue to store its content <emphasis>in situ</emphasis> after
173        construction and after assignment involving identical contained types,
174        <code>variant</code> would store its content on the heap after
175        assignment involving distinct contained types.</para>
176
177      <para>The algorithm for assignment would proceed as follows:
178
179        <orderedlist>
180          <listitem>Copy-construct the content of the right-hand side to the
181            heap; call the pointer to this data <code>p</code>.</listitem>
182          <listitem>Destroy the content of the left-hand side.</listitem>
183          <listitem>Copy <code>p</code> to the left-hand side
184            storage.</listitem>
185        </orderedlist>
186
187        Since all operations on pointers are nothrow, this scheme would allow
188        <code>variant</code> to meet its never-empty guarantee.
189      </para>
190
191      <para>The most obvious concern with this approach is that while it
192        certainly eliminates the space overhead of double storage, it
193        introduces the overhead of dynamic-allocation to <code>variant</code>
194        assignment -- not just in terms of the initial allocation but also
195        as a result of the continued storage of the content on the heap. While
196        the former problem is unavoidable, the latter problem may be avoided
197        with the following &quot;temporary heap backup&quot; technique:
198
199        <orderedlist>
200          <listitem>Copy-construct the content of the
201            <emphasis>left</emphasis>-hand side to the heap; call the pointer to
202            this data <code>backup</code>.</listitem>
203          <listitem>Destroy the content of the left-hand side.</listitem>
204          <listitem>Copy-construct the content of the right-hand side in the
205            (now-empty) storage of the left-hand side.</listitem>
206          <listitem>In the event of failure, copy <code>backup</code> to the
207            left-hand side storage.</listitem>
208          <listitem>In the event of success, deallocate the data pointed to
209            by <code>backup</code>.</listitem>
210        </orderedlist>
211      </para>
212
213      <para>With this technique: 1) only a single storage is used;
214        2) allocation is on the heap in the long-term only if the assignment
215        fails; and 3) after any <emphasis>successful</emphasis> assignment,
216        storage within the <code>variant</code> is guaranteed. For the
217        purposes of the initial release of the library, these characteristics
218        were deemed a satisfactory compromise solution.</para>
219
220      <para>There remain notable shortcomings, however. In particular, there
221        may be some users for which heap allocation must be avoided at all
222        costs; for other users, any allocation may need to occur via a
223        user-supplied allocator. These issues will be addressed in the future
224        (see <xref linkend="variant.design.never-empty.roadmap"/>). For now,
225        though, the library treats storage of its content as an implementation
226        detail. Nonetheless, as described in the next section, there
227        <emphasis>are</emphasis> certain things the user can do to ensure the
228        greatest efficiency for <code>variant</code> instances (see
229        <xref linkend="variant.design.never-empty.optimizations"/> for
230        details).</para>
231    </section>
232
233    <section id="variant.design.never-empty.optimizations">
234      <title>Enabling Optimizations</title>
235
236      <para>As described in
237        <xref linkend="variant.design.never-empty.problem"/>, the central
238        difficulty in implementing the never-empty guarantee is the
239        possibility of failed copy-construction during <code>variant</code>
240        assignment. Yet types with nothrow copy constructors clearly never
241        face this possibility. Similarly, if one of the bounded types of the
242        <code>variant</code> is nothrow default-constructible, then such a
243        type could be used as a safe &quot;fallback&quot; type in the event of
244        failed copy construction.</para>
245
246      <para>Accordingly, <code>variant</code> is designed to enable the
247        following optimizations once the following criteria on its bounded
248        types are met:
249
250        <itemizedlist>
251
252          <listitem>For each bounded type <code>T</code> that is nothrow
253            copy-constructible (as indicated by
254            <code><classname>boost::has_nothrow_copy</classname></code>), the
255            library guarantees <code>variant</code> will use only single
256            storage and in-place construction for <code>T</code>.</listitem>
257
258          <listitem>If <emphasis>any</emphasis> bounded type is nothrow
259            default-constructible (as indicated by
260            <code><classname>boost::has_nothrow_constructor</classname></code>),
261            the library guarantees <code>variant</code> will use only single
262            storage and in-place construction for <emphasis>every</emphasis>
263            bounded type in the <code>variant</code>. Note, however, that in
264            the event of assignment failure, an unspecified nothrow
265            default-constructible bounded type will be default-constructed in
266            the left-hand side operand so as to preserve the never-empty
267            guarantee.</listitem>
268
269        </itemizedlist>
270
271      </para>
272
273      <para><emphasis role="bold">Caveat</emphasis>: On most platforms, the
274        <libraryname>Type Traits</libraryname> templates
275        <code>has_nothrow_copy</code> and <code>has_nothrow_constructor</code>
276        by default return <code>false</code> for all <code>class</code> and
277        <code>struct</code> types. It is necessary therefore to provide
278        specializations of these templates as appropriate for user-defined
279        types, as demonstrated in the following:
280
281<programlisting>// ...in your code (at file scope)...
282
283namespace boost {
284
285  template &lt;&gt;
286  struct <classname>has_nothrow_copy</classname>&lt; myUDT &gt;
287    : <classname>mpl::true_</classname>
288  {
289  };
290
291}
292</programlisting>
293
294      </para>
295
296      <para><emphasis role="bold">Implementation Note</emphasis>: So as to make
297        the behavior of <code>variant</code> more predictable in the aftermath
298        of an exception, the current implementation prefers to default-construct
299        <code><classname>boost::blank</classname></code> if specified as a
300        bounded type instead of other nothrow default-constructible bounded
301        types. (If this is deemed to be a useful feature, it will become part
302        of the specification for <code>variant</code>; otherwise, it may be
303        obsoleted. Please provide feedback to the Boost mailing list.)</para>
304    </section>
305
306    <section id="variant.design.never-empty.roadmap">
307      <title>Future Direction: Policy-based Implementation</title>
308
309      <para>As the previous sections have demonstrated, much effort has been
310        expended in an attempt to provide a balance between performance, data
311        size, and heap usage. Further, significant optimizations may be
312        enabled in <code>variant</code> on the basis of certain traits of its
313        bounded types.</para>
314
315      <para>However, there will be some users for whom the chosen compromise
316        is unsatisfactory (e.g.: heap allocation must be avoided at all costs;
317        if heap allocation is used, custom allocators must be used; etc.). For
318        this reason, a future version of the library will support a
319        policy-based implementation of <code>variant</code>. While this will
320        not eliminate the problems described in the previous sections, it will
321        allow the decisions regarding tradeoffs to be decided by the user
322        rather than the library designers.</para>
323    </section>
324
325  </section>
326
327</section>
Note: See TracBrowser for help on using the repository browser.