design.xml 16 KB

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