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