rationale.qbk 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914
  1. [/license
  2. Boost.Bimap
  3. Copyright (c) 2006-2007 Matias Capeletto
  4. Distributed under the Boost Software License, Version 1.0.
  5. (See accompanying file LICENSE_1_0.txt or copy at
  6. http://www.boost.org/LICENSE_1_0.txt)
  7. ]
  8. [/ QuickBook Document version 1.4 ]
  9. [section Rationale]
  10. This section assumes that you have read all other sections, the most of
  11. important of which being ['tutorial], ['std::set theory] and the ['reference],
  12. and that you have tested the library. A lot of effort was invested in
  13. making the interface as intuitive and clean as possible. If you
  14. understand, and hopefully like, the interface of this library, it will
  15. be a lot easier to read this rationale. The following section is little
  16. more than a rationale. This library was coded in the context of the
  17. Google SoC 2006 and the student and mentor were in different continents.
  18. A great deal of email flowed between Joaquin and Matias. The juiciest
  19. parts of the conversations where extracted and rearranged here.
  20. [note To browse the code, you can use the [@doxydoc/index.html ['Bimap Complete Reference]], a
  21. doxygen-powered document targeted at developers.
  22. ]
  23. [section General Design]
  24. The initial explanation includes few features. This section aims to
  25. describe the general design of the library and excludes details of those
  26. features that are of lesser importance; these features will be
  27. introduced later.
  28. The design of the library is divided into two parts. The first is the
  29. construction of a [^relation] class. This will be the object stored and
  30. managed by the [^multi_index_container] core. The idea is to make this
  31. class as easy as possible to use, while making it efficient in terms of
  32. memory and access time. This is a cornerstone in the design of
  33. [*Boost.Bimap] and, as you will see in this rationale, the rest of the
  34. design follows easily.
  35. The following interface is necessary for the [^relation] class:
  36. typedef -unspecified- TA; typedef -unspecified- TB;
  37. TA a, ai; TB b, bi;
  38. typedef relation< TA, TB > rel;
  39. STATIC_ASSERT( is_same< rel::left_type , TA >::value );
  40. STATIC_ASSERT( is_same< rel::right_type, TB >::value );
  41. rel r(ai,bi);
  42. assert( r.left == ai && r.right == bi );
  43. r.left = a; r.right = b;
  44. assert( r.left == a && r.right == b );
  45. typedef pair_type_by< member_at::left , rel >::type pba_type;
  46. STATIC_ASSERT( is_same< pba_type::first_type , TA >::value );
  47. STATIC_ASSERT( is_same< pba_type::second_type, TB >::value );
  48. typedef pair_type_by< member_at::right, rel >::type pbb_type;
  49. STATIC_ASSERT( is_same< pbb_type::first_type , TB >::value );
  50. STATIC_ASSERT( is_same< pbb_type::second_type, TA >::value );
  51. pba_type pba = pair_by< member_at::left >(r);
  52. assert( pba.first == r.left && pba.second == r.right );
  53. pbb_type pbb = pair_by< member_at::right >(r);
  54. assert( pbb.first == r.right && pbb.second == r.left );
  55. __RELATION__
  56. Although this seems straightforward, as will be seen later, it is the
  57. most difficult code hack of the library. It is indeed very easy if we
  58. relax some of the efficiency constraints. For example, it is trivial if
  59. we allow a relation to have greater size than the the sum of those of
  60. its components. It is equally simple if access speed is not important.
  61. One of the first decisions made about [*Boost.Bimap] was, however, that, in
  62. order to be useful, it had to achieve zero overhead over the wrapped
  63. [*Boost.MultiIndex] container. Finally, there is another constraint that
  64. can be relaxed: conformance to C++ standards, but this is quite
  65. unacceptable. Let us now suppose that we have coded this class, and it
  66. conforms to what was required.
  67. The second part is based on this [^relation] class. We can now view the
  68. data in any of three ways: `pair<A,B>`, `relation<A,B>` and `pair<B,A>`.
  69. Suppose that our bimap supports only one-to-one relations. (Other
  70. relation types are considered additional features in this design.)
  71. The proposed interface is very simple, and it is based heavily on the
  72. concepts of the STL. Given a `bimap<A,B> bm`:
  73. # `bm.left` is signature-compatible with a `std::map<A,B>`
  74. # `bm.right` is signature-compatible with a `std::map<B,A>`
  75. # `bm` is signature-compatible with a `std::set<relation<A,B> >`
  76. __SIMPLE_BIMAP__
  77. This interface is easily learned by users who have a STL background, as
  78. well as being simple and powerful. This is the general design.
  79. [heading Relation Implementation]
  80. This section explains the details of the actual [^relation] class implementation.
  81. The first thing that we can imagine is the use of an [^union]. Regrettably,
  82. the current C++ standard only allows unions of POD types. For the views,
  83. we can try a wrapper around a `relation<A,B>` that has two references
  84. named first and second that bind to `A` and `B`, or to `B` and `A`.
  85. relation<TA,TB> r;
  86. const_reference_pair<A,B> pba(r);
  87. const_reference_pair<B,A> pbb(r);
  88. It is not difficult to code the relation class using this, but two
  89. references are initialized at every access and using of `pba.first` will
  90. be slower in most compilers than using `r.left` directly . There is
  91. another hidden drawback of using this scheme: it is not
  92. iterator-friendly, since the map views iterators must be degraded to
  93. ['Read Write] instead of ['LValue]. This will be explained later.
  94. At first, this seems to be the best we can do with the current C++
  95. standard. However there is a solution to this problem that does not
  96. conform very well to C++ standards but does achieve zero overhead in
  97. terms of access time and memory, and additionally allows the view
  98. iterators to be upgraded to ['LValue] again.
  99. In order to use this, the compiler must conform to a
  100. layout-compatibility clause that is not currently in the standard but is
  101. very natural. The additional clause imposes that if we have two classes:
  102. struct class_a_b
  103. {
  104. Type1 name_a;
  105. Type2 name_b;
  106. };
  107. struct class_b_a
  108. {
  109. Type1 name_b;
  110. Type2 name_a;
  111. };
  112. then the storage layout of [^class_a_b] is equal to the storage layout of
  113. [^class_b_a]. If you are surprised to learn that this does not hold in a
  114. standards-compliant C++ compiler, welcome to the club. It is the natural
  115. way to implement it from the point of view of the compiler's vendor and
  116. is very useful for the developer. Maybe it will be included in the
  117. standard some day. Every current compiler conforms to this.
  118. If we are able to count on this, then we can implement an idiom called
  119. [^mutant]. The idea is to provide a secure wrapper around [^reinterpret_cast].
  120. A class can declare that it can be viewed using different view classes
  121. that are storage-compatible with it. Then we use the free function
  122. [^mutate<view>(mutant)] to get the view. The `mutate` function checks at
  123. compile time that the requested view is declared in the mutant views list.
  124. We implement a class name `structured_pair` that is signature-compatible
  125. with a `std::pair`, while the storage layout is configured with a third
  126. template parameter. Two instances of this template class will provide
  127. the views of the relation.
  128. The thing is that if we want to be standards-compliant, we cannot use
  129. this approach. It is very annoying not to be able to use something that
  130. we know will work with every compiler and that is far better than
  131. alternatives. So -- and this is an important decision -- we have to find
  132. a way to use it and still make the library standards-compliant.
  133. The idea is very simple. We code both approaches: the
  134. const_reference_pair-based and the mutant-based, and use the mutant
  135. approach if the compiler is compliant with our new layout-compatible
  136. clause. If the compiler really messes things up, we degrade the
  137. performance of the bimap a little. The only drawback here is that, while
  138. the mutant approach allows to make ['LValue] iterators, we have to degrade
  139. them to ['Read Write] in both cases, because we require that the same code
  140. be compilable by any standards-compliant compiler.
  141. [note
  142. Testing this approach in all the supported compilers indicated that the
  143. mutant idiom was always supported. The strictly compliant version was
  144. removed from the code because it was never used.
  145. ]
  146. [heading Bimap Implementation]
  147. The core of bimap will be obviously a `multi_index_container`. The basic
  148. idea to tackle the implementation of the bimap class is to use
  149. [^iterator_adaptor] to convert the iterators from Boost.MultiIndex to the
  150. `std::map` and `std::set` behaviour. The `map_view` and the `set_view` can be
  151. implemented directly using this new transformed iterators and a wrapper
  152. around each index of the core container. However, there is a hidden
  153. idiom here, that, once coded, will be very useful for other parts of
  154. this library and for Boost.MRU library. Following the ideas from
  155. `iterator_adaptor`, Boost.Bimap views are implemented using a
  156. [^container_adaptor]. There are several template classes (for example
  157. `map_adaptor` and `set_adaptor`) that take a `std::map` signature-conformant
  158. class and new iterators, and adapt the container so it now uses this
  159. iterators instead of the originals. For example, if you have a
  160. `std::set<int*>`, you can build other container that behaves exactly as a
  161. `std::set<int>` using `set_adaptor` and [^iterator_adaptor]. The combined use
  162. of this two tools is very powerful. A [^container_adaptor] can take classes
  163. that do not fulfil all the requirements of the adapted container. The
  164. new container must define these missing functions.
  165. [endsect]
  166. [section Additional Features]
  167. [heading N-1, N-N, hashed maps]
  168. This is a very interesting point of the design. The framework introduced
  169. in ['std::set theory] permits the management of the different constraints
  170. with a very simple and conceptual approach. It is easy both to remember
  171. and to learn. The idea here is to allow the user to specify the collection type
  172. of each key directly. In order to implement this feature, we have to
  173. solve two problems:
  174. * The index types of the `multi_index_container` core now depends on
  175. the collection type used for each key.
  176. * The map views now change their semantics according to the collection type
  177. chosen.
  178. Boost.Bimap relies heavily on Boost.MPL to implement all of the
  179. metaprogramming necessary to make this framework work. By default, if
  180. the user does not specify the kind of the set, a `std::set` type is used.
  181. __BIMAP_STRUCTURES__
  182. [heading Collection type of relation constraints]
  183. The constraints of the bimap set view are another very important
  184. feature. In general, Boost.Bimap users will base the set view type on
  185. one of the two collection types of their keys. It may be useful however to give
  186. this set other constraints or simply to order it differently. By
  187. default, Boost.Bimap bases the collection type of relations on the left collection
  188. type, but the user may choose between:
  189. * left_based
  190. * right_based
  191. * set_of_relation<>
  192. * multiset_of_relation<>
  193. * unordered_set_of_relation<>
  194. * unordered_multiset_of_relation<>
  195. * list_of
  196. * vector_of
  197. In the first two cases, there are only two indices in the
  198. `multi_index_core`, and for this reason, these are the preferred options.
  199. The implementation uses further metaprogramming to define a new index if
  200. necessary.
  201. [/
  202. [heading Hooking Data]
  203. This is one of the things that makes Boost.Bimap very appealing in
  204. tackling a problem. In general, programmers use maps to access
  205. information quickly. Boost.Bimap allows the user to hook data inside the
  206. bimap so that it is not necessary to maintain another map. The
  207. implementation is based heavily on metaprogramming.
  208. ]
  209. [heading Tagged]
  210. The idea of using tags instead of the [^member_at::side] idiom is very
  211. appealing since code that uses it is more readable. The only cost is
  212. compile time. ['boost/bimap/tagged] is the implementation of a non-invasive
  213. tagged idiom. The [^relation] class is built in such a way that even when
  214. the user uses tags, the [^member_at::side] idiom continues to work. This is
  215. good since an user can start tagging even before completing the coding
  216. of the algorithm, and the untagged code continues to work. The
  217. development becomes a little more complicated when user-defined tags are
  218. included, but there are many handy metafunctions defined in the [^tagged]
  219. idiom that help to keep things simple enough.
  220. __TAGGED__
  221. [endsect]
  222. [section Code]
  223. You can browse the code using the [@doxydoc/index.html [*Boost.Bimap doxygen docs]].
  224. The code follows the [@http://www.boost.org/more/lib_guide.htm Boost Library Requirement and Guidelines] as
  225. closely as possible.
  226. [table folders in boost/bimap
  227. [[name][what is inside?]]
  228. [[/ ][user level header files ]]
  229. [[tagged/ ][tagged idiom ]]
  230. [[relation/ ][the bimap data ]]
  231. [[container_adaptor/ ][easy way of adapting containers ]]
  232. [[views/ ][bimap views ]]
  233. [[property_map/ ][support for property map concept ]]
  234. ]
  235. [table folders in each folder
  236. [[name][what is inside?]]
  237. [[ ][class definitions]]
  238. [[support/ ][optional metafunctions and free functions]]
  239. [[detail/ ][things not intended for the user's eyes]]
  240. ]
  241. [endsect]
  242. [section The student and the mentor]
  243. [tip It is a good idea to read the original
  244. [@http://h1.ripway.com/mcape/boost/libs/misc/ Boost.Misc SoC proposal] first.]
  245. [:[^- The discussion starts with Joaquin trying to strip out the "misc" name out of the library -]]
  246. __JOAQUIN_PHOTO__
  247. [*Joaquin]
  248. [:['
  249. Thinking about it, the unifying principle of MISC containers is perhaps
  250. misleading: certainly all miscs use multi-indexing internally, but this does
  251. not reflect much in the external interface (as it should be, OTOH). So, from
  252. the user's point of view, miscs are entirely heterogeneous beasts. Moreover,
  253. there isn't in your proposal any kind of global facility common to all miscs.
  254. What about dropping the misc principle and working on each container as a
  255. separate library, then? You'd have boost::bimap, boost::mru, etc, and no common
  256. intro to them. This also opens up the possibility to add other containers to
  257. the suite which aren't based on B.MI. What's your stance on this? Do you see a
  258. value in keeping miscs conceptually together?
  259. ]]
  260. __MATIAS_PHOTO__
  261. [*Matias]
  262. [:['
  263. As the original proposal states only two containers (bimap and mru set) both
  264. based in B.MI, it was straight forward to group them together. When I was
  265. writing the SoC proposal I experienced a similar feeling when the two families
  266. begin to grow. As you say, the only common denominator is their internal
  267. implementation. I thought a bit about a more general framework to join this two
  268. families (and other internally related ones) and finally came up with an idea:
  269. Boost.MultiIndex! So I think that it is not a good idea to try to unify the two
  270. families and I voted in favor of get rid of the misc part of boost::misc::bimap
  271. and boost::misc::mru. Anyway, for my SoC application it seems OK to put the
  272. two families in the same project because although from the outside they are
  273. completely unrelated, the work I will have to do in order to build the libraries
  274. will be consistent and what I will learn coding the bimap family will be used
  275. when I start to code the mru family. When the mru family is in place, I will
  276. surely have learnt other things to improve the bimap group.
  277. ]]
  278. [:['
  279. On the other hand, I think it will be useful for the general user to
  280. have at least some document linked in the B.MI documentation that
  281. enumerates the most common cases of uses (a bimap and an mru set for
  282. example) and points where to find clean implementation for this useful
  283. containers. For now, a link to boost::bimap and other one to boost::mru
  284. will suffice. If you think about the title of such a document,
  285. you will probably come up with something like: Common Multi Index
  286. Specialized Containers, and we are back to our misc proposal.
  287. So, to order some ideas:
  288. ]]
  289. [:['- A new family of containers that can be accessed by both key will
  290. be created. (boost::bimap)]]
  291. [:['- A new family of time aware containers will see the light.
  292. (boost::mru)]]
  293. [:['- A page can be added to B.MI documentation, titled misc that links
  294. this new libraries.]]
  295. [:['
  296. This is a clearer framework for the user. They can use a mru container
  297. without hearing about Boost.MultiIndex at all.
  298. And B.MI users will get some of their common containers already
  299. implemented with an STL friendly interface in other libraries.
  300. And as you stated this is more extensible because opens the door to use
  301. other libraries in bimap and mru families than just Boost.MultiIndex
  302. without compromising the more general boost framework.
  303. The word "misc" it is going to disappear from the code and
  304. the documentation of bimap and mru. From now on the only use for it will be to
  305. identify our SoC project. I am thinking in a name for the bimap library.
  306. What about Boost.BidirectionalMap? Ideas?
  307. ]]
  308. [*Joaquin]
  309. [:['
  310. Yes, Boost.Bimap. In my opinion, bimap is a well known name
  311. in the Boost and even in the C++ community. It sounds and is short. Why not to
  312. vindicate yourself as the owner of this name?
  313. ]]
  314. [^- Then after a week of work -]
  315. [*Matias]
  316. [:['
  317. Now that Boost.Bimap is getting some shape, I see that as
  318. you have told me, we must offer a "one_to_many_map" and a "multi_bimap"
  319. as part of the library. The framework I am actually working allowed to
  320. construct this kind of bidirectional maps and it is easy to understand from
  321. the user side.
  322. ]]
  323. [*Joaquin]
  324. [:['
  325. OK, I am glad we agree on this point.
  326. ]]
  327. [*Matias]
  328. [:['
  329. With respect to the symmetry of the key access names, I have to
  330. agree that there is not much a difference between the following ones:
  331. ]]
  332. [:['- to - from]]
  333. [:['- to - b]]
  334. [:['- 0 - 1]]
  335. [:['- left - right]]
  336. [:['
  337. In my opinion it is a matter of taste, but left/right sounds more symmetrical than
  338. the others.
  339. ]]
  340. [*Joaquin]
  341. [:['
  342. I like very much the left/right notation, it is very simple to
  343. remember and it is a lot more symmetrical than to/from.
  344. ]]
  345. [*Matias]
  346. [:['
  347. At first my idea was to obtain ease of use hiding the B.MI
  348. core, making it more STL-intuitive. Nevertheless I have realized
  349. that B.MI is a lot more coherent and easy to use that I had imagined. This
  350. makes me think again in the problem. In the design that I am coding now, bimap
  351. *is-a* multi_index_container specializes with a data type very comfortable
  352. called bipair, that can be seen like any of the two maps that integrates it
  353. using map views. This scheme has great benefits for users:
  354. ]]
  355. [:['
  356. - If the user already knows B.MI, he can take advantage of the tools that
  357. it provides and that are not present in the STL containers. In addition, in some
  358. cases the use to indices to see the data can be very useful.
  359. ]]
  360. [:['
  361. - If the user does not know anything about B.MI but have an STL framework,
  362. the learning curve is reduced to understand the bimap instantiation and how a
  363. is obtained the desired map view.
  364. ]]
  365. [:['
  366. Another very important benefit holds: All the algorithms done for
  367. B.MI continues to work with Boost.Bimap and if B.MI continues growing, bimap
  368. grow automatically.
  369. ]]
  370. [*Joaquin]
  371. [:['
  372. Umm... This is an interesting design decision, but
  373. controversial in my opinion. Basically you decide to expose the
  374. implementation of bimap; that has advantages, as you stated, but also
  375. a nonsmall disadvantage: once *you have documented* the implementation,
  376. it is not possible to change it anymore. It is a marriage with B.MI without
  377. the chance of divorce. The other possibility, to hide the implementation and
  378. to duplicate and document the provided functionality, explicitly or
  379. implicitly due to the same characteristics of the implementation, is
  380. of course heavier to maintain, but it gives a degree of freedom to change
  381. the guts of your software if you need to. Do not take this like a frontal
  382. objection, but I think that it is quite important design decision, not only
  383. in the context of bimap but in general.
  384. ]]
  385. [*Matias]
  386. [:['
  387. You are quite right here. I think we have to choose the hardest
  388. path and hide the B.MI core from the user. I am sending you the first draft of
  389. bimap along with some documentation.
  390. ]]
  391. [^- This completes the second week, the documentation was basically the first
  392. section of this rationale -]
  393. [*Joaquin]
  394. [:['
  395. I must confess that I am beginning to like what I see.
  396. I am mathematical by vocation, and when I see symmetry in a formulation
  397. I believe that it is in the right track.
  398. ]]
  399. [*Matias]
  400. [:['
  401. We are two mathematicians by vocation then.
  402. ]]
  403. [*Joaquin]
  404. [:['
  405. I think that the part of std::set theory is very clear.
  406. To me, it turns out to me somewhat strange to consider the rank of a map
  407. (values X) like a std::set, but of course the formulation is consistent.
  408. ]]
  409. [*Matias]
  410. [:['
  411. I like it very much, it can be a little odd at first, but
  412. now that I have get used to it, it is very easy to express in the code my
  413. contrains on the data, and I believe that if somebody reads the code and
  414. sees the bimap instantiation he is not going to have problems understanding
  415. it. Perhaps it is easier to understand it if we use your notation:
  416. ordered_nonunique, unordered_unique, but this goes against our STL facade.
  417. In my opinion the user that comes from STL must have to learn as less as possible.
  418. ]]
  419. [*Joaquin]
  420. [:['
  421. Considering a relation like a `struct {left, right}`
  422. is clean and clear. If I understand it well, one relation has views of type
  423. `pair{first, second}`, is this correct?
  424. ]]
  425. [*Matias]
  426. [:['
  427. Yes, I believe that the left/right notation to express symmetry
  428. is great. I believe that to people is going to love it.
  429. ]]
  430. [*Joaquin]
  431. [:['
  432. OK, perfect. I likes this very much:
  433. ]]
  434. [:['- bm.left is compatible with std::map<A,B>]]
  435. [:['- bm.right is compatible with std::map<B,A>]]
  436. [:['- bm is compatible with std::set<relation<A,B>>]]
  437. [:['
  438. It is elegant and symmetric. I feel good vibrations here.
  439. ]]
  440. [*Matias]
  441. [:['
  442. Great!
  443. ]]
  444. [*Joaquin]
  445. [:['
  446. Moving on, the support for N-1, N-N, and hashed index is very easy
  447. to grasp, and it fits well in framework.
  448. However I do not finish to understand very well the "set<relation> constraints" section.
  449. Will you came up with some examples of which is the meaning of the different
  450. cases that you enumerate?
  451. ]]
  452. [*Matias - ]
  453. [:['
  454. Yes, I mean:
  455. ]]
  456. [:['- based on the left]]
  457. [:['- based on the right]]
  458. [:['
  459. The bimap core must be based on some index of multi index. If the index
  460. of the left is of the type hash, then in fact the main view is going
  461. to be an unordered_set< relation<A,B> >. Perhaps this is not what the user
  462. prefers and he wants to base its main view on the right index.
  463. ]]
  464. [:['- set_of_relation ]]
  465. [:['- multiset_of_relation ]]
  466. [:['- unordered_set_of_relation ]]
  467. [:['- unordered_multiset_of_relation ]]
  468. [:['
  469. However, if both of them are hash indexes, the user may want the main view
  470. to be ordered. As we have a B.MI core this is very easy to support, we just have
  471. to add another index to it.
  472. ]]
  473. [*Joaquin]
  474. [:['
  475. I understand it now. OK, I do not know if we have to include this
  476. in the first version, is going to be a functionality avalanche!
  477. ]]
  478. [*Matias]
  479. [:['
  480. The user is not affected by the addition of this functionality,
  481. because by default it will be based on the left index that is a very natural
  482. behaviour. I do not think that this is functionality bloat, but I agree with
  483. you that it is a functionality avalanche.
  484. ]]
  485. [*Joaquin]
  486. [:['
  487. There are restrictions between the left and right set types
  488. and the possible main view set types. For example if some of the index is
  489. of unique type, then the main view cannot be of type multiset_of_relation.
  490. To the inverse one, if the main view is of type set_of_relation the left and
  491. the right index cannot be of type multi_set. All this subject of the unicity
  492. constrictions and the resulting interactions between indexes is one of the subtle
  493. subjects of B.MI.
  494. ]]
  495. [*Matias]
  496. [:['
  497. This can be checked at compile time and informed as an error
  498. in compile time.
  499. ]]
  500. [*Joaquin]
  501. [:['
  502. It can be interesting.
  503. ]]
  504. [^- And right when everything seems to be perfect... - ]
  505. [*Joaquin]
  506. [:['
  507. I have some worse news with respect to mutant, it is very a
  508. well designed and manageable class, unfortunately, C++ does not guarantee
  509. layout-compatibility almost in any case. For example, the C++ standard does
  510. not guarantee that the classes `struct{T1 a; T2 b;}` and `struct{T1 b; T2 a;}`
  511. are layout-compatible, and therefore the trick of reinterpret_cast is an
  512. undefined behavior. I am with you in which that in the 100% of the cases
  513. this scheme will really work, but the standard is the standard. If you can
  514. look the layout-compatibility subject in it (http://www.kuzbass.ru/docs/isocpp/).
  515. As you see, sometimes the standard is cruel. Although mutant seems a lost case,
  516. please do not hurry to eliminate it. We will see what can be done for it.
  517. ]]
  518. [*Matias]
  519. [:['
  520. I read the standard, and you were right about it. Mutant was an implementation
  521. detail. It is a pity because I am sure that it will work perfect in any compiler.
  522. Perhaps the standard becomes more strict some day and mutant returns to life...
  523. We can then try a wrapper around a relation<A,B> that have two references named
  524. first and second that bind to A and B, or B and A.
  525. ]]
  526. ``
  527. relation<TA,TB> r;
  528. const_reference_pair<A,B> pba(r);
  529. const_reference_pair<B,A> pbb(r);
  530. ``
  531. [:['
  532. It is not difficult to code the relation class in this way but two references
  533. are initialized with every access and the use of `pba.first` will be slower
  534. than `r.left` in most compilers. It is very difficult to optimize this kind of
  535. references.
  536. ]]
  537. [*Joaquin]
  538. [:['
  539. This workaround is not possible, due to technical problems with
  540. the expected behavior of the iterators. If the iterators of bm.left are of
  541. bidirectional type, then standard stated that it have to return an object of type
  542. const value_type& when dereferenced. You will have to return a const_reference_pair
  543. created in the flight, making it impossible to return a reference.
  544. ]]
  545. [*Matias]
  546. [:['
  547. I understand... I have workaround for that also but surely
  548. the standard will attack me again! We must manage to create the class relation
  549. that responds as we want, the rest of the code will flow from this point.
  550. This clear separation between the relation class and the rest of the library,
  551. is going to help to us to separate the problems and to attack them better.
  552. ]]
  553. [*Joaquin]
  554. [:['
  555. What workaround? It already pricks my curiosity,I have dedicated
  556. a long time to the subject and I do not find any solution except that we
  557. allow the relation class to occupy more memory.
  558. ]]
  559. [*Matias]
  560. [:['
  561. We must achieve that the relation<A,B> size equals the pair<A,B> size
  562. if we want this library to be really useful. I was going to write my workaround and
  563. I realized that It does not work. Look at this:
  564. http://www.boost.org/libs/iterator/doc/new-iter-concepts.html
  565. Basically the problem that we are dealing is solved if we based our iterators on
  566. this proposal. The present standard forces that the bidirectional iterators also
  567. are of the type input and output. Using the new concepts there is no inconvenient
  568. in making our iterators "Readable Writable Swappable Bidirectional Traversal".
  569. Therefore the const_reference_pair returns to be valid.
  570. ]]
  571. [*Joaquin]
  572. [:['
  573. It is correct in the sense that you simply say that
  574. your iterators are less powerful than those of the std::map. It is
  575. not that it is wrong, simply that instead of fixing the problem, you
  576. confess it.
  577. ]]
  578. [*Matias]
  579. [:['
  580. OK, but in our particular case; What are the benefits
  581. of offering a LValue iterator against a Read Write iterator?
  582. It does not seem to me that it is less powerful in this case.
  583. ]]
  584. [*Joaquin]
  585. [:['
  586. The main problem with a ReadWrite is that the following thing:
  587. `value_type * p=&(*it);`
  588. fails or stores a transitory direction in p. Is this important in the real life?
  589. I do not know. How frequently you store the direction of the elements of a map?
  590. Perhaps it is not very frequent, since the logical thing is to store the
  591. iterators instead of the directions of the elements.
  592. Let us review our options:
  593. ]]
  594. [:['
  595. 1. We used mutant knowing that is not standard, but of course it is
  596. supported in the 100% of the cases.
  597. ]]
  598. [:['
  599. 2. We used const_reference_pair and we declared the iterators not LValue.
  600. ]]
  601. [:['
  602. 3. We found some trick that still we do not know. I have thus been playing
  603. with unions and things, without much luck.
  604. ]]
  605. [:['
  606. 4. We leverage the restriction that views have to support the first, second
  607. notation. If we made this decision, there are several possibilities:
  608. ]]
  609. [:['
  610. ''' '''a. The left map has standard semantics first/second while the right map
  611. has the inverse semantics.
  612. ]]
  613. [:['
  614. ''' '''b. Instead of first and second we provide first() and second(), with
  615. which the problem is trivial.
  616. ]]
  617. [:['
  618. ''' '''c. The map view do not support first/second but left/right as the
  619. father relation
  620. ]]
  621. [:['
  622. 5. We solve the problem using more memory than sizeof(pair<A,B>).
  623. ]]
  624. [:['
  625. In any case, I would say that the only really unacceptable option is the last one.
  626. ]]
  627. [*Matias]
  628. [:['
  629. Lets see.
  630. ]]
  631. [:['
  632. 1. I want the "standard compliant" label in the library.
  633. ]]
  634. [:['
  635. 2. This is the natural choice, but knowing that there is another option
  636. that always works and it is more efficient is awful.
  637. ]]
  638. [:['
  639. 3. I have also tried to play with unions, the problem is that the union members
  640. must be POD types.
  641. ]]
  642. [:['
  643. 4. This option implies a big lost to the library.
  644. ]]
  645. [:['
  646. 5. Totally agree.
  647. ]]
  648. [:['
  649. I want to add another option to this list. Using metaprogramming,
  650. the relation class checks if the compiler supports the mutant idiom.
  651. If it supports it then it uses it and obtains zero overhead
  652. plus LValue iterators, but if it do not supports it then uses
  653. const_reference_pair and obtains minimum overhead with ReadWrite iterators.
  654. This might be controversial but the advantages that mutant offers are very big
  655. and the truth is that I do not believe that in any actual compiler this idiom is
  656. not supported. This scheme would adjust perfectly to the present standard
  657. since we are not supposing anything. The only drawback here is that although
  658. the mutant approach allows to make LValue iterators we have to degrade they
  659. to Read Write in both cases, because we want that the same code can be
  660. compiled in any standard compliant compiler.
  661. ]]
  662. [^- Hopefully we find our way out of the problem -]
  663. [*Joaquin]
  664. [:['
  665. Changing the subject, I believe that the general concept of hooking data
  666. is good, but I do not like the way you implement it. It has to be easy
  667. to migrate to B.MI to anticipate the case in that Boost.Bimap becomes insufficient.
  668. It is more natural for a B.MI user that the data is accessed without the indirection
  669. of `.data`. I do not know how this can be articulated in your framework.
  670. ]]
  671. [*Matias]
  672. [:['
  673. I have a technical problem to implement the data_hook in this way.
  674. If the standard would let us use the mutant idiom directly, I can implement it
  675. using multiple inheritance. But as we must use const_reference_pair too, It becomes
  676. impossible for me to support it. We have three options here:
  677. ]]
  678. [:['
  679. 1) relation { left, right, data } and pair_view { first, second, data }
  680. ]]
  681. [:['
  682. - This is more intuitive within the bimap framework, since it does not
  683. mix the data with the index, as a table in a data base does, but gives more importance to
  684. the index.
  685. ]]
  686. [:['
  687. - It is not necessary that the user puts the mutable keyword in each member of
  688. the data class.
  689. ]]
  690. [:['
  691. - This moves away just a little bit from B.MI because the model
  692. of it is similar to a table, but it continues to exist a clear path of migration.
  693. ]]
  694. [:['
  695. 2) relation { left,right, d1,d2... dn } and pair_view { first, second, data }
  696. ]]
  697. [:['
  698. - The path to B.MI is the one you have proposed.
  699. ]]
  700. [:['
  701. - It is very asymmetric. It is necessary to explain that the views are
  702. handled different that the relation.
  703. ]]
  704. [:['
  705. - The user must place the mutable keyboards in the data class.
  706. ]]
  707. [:['
  708. 3) Only relation { left,right, d1,d2... dn }
  709. ]]
  710. [:['
  711. - Simple migration path to B.MI.
  712. ]]
  713. [:['
  714. - You are not able to access the hooked data from the views.
  715. ]]
  716. [:['
  717. My vote goes to the first proposal.
  718. ]]
  719. [*Joaquin]
  720. [:['
  721. Yes, the first option is the one that less surprises hold to the user.
  722. I also vote for 1.
  723. ]]
  724. [^- The third week was over -]
  725. [*Matias]
  726. [:['
  727. There is still one problem that I have to solve. I need to
  728. know if it is necessary to create a map_view associated to nothing. If
  729. it is necessary there are two options: that it behaves as an empty container or
  730. that it throws an exception or assert when trying to use it. If it is not necessary,
  731. the map_view is going to keep a reference instead of a pointer.
  732. To me, the map_view always must be viewing something. In the case of the iterators
  733. being able to create them empty, makes them easy to use in contexts that require
  734. constructors by default, like being the value_type of a container, but I do not
  735. believe that this is the case of map_view.
  736. ]]
  737. [*Joaquin]
  738. [:['
  739. How would an empty map_view be useful? My intuition is like yours,
  740. map_view would have to be always associate to something. If we wished to obtain
  741. the semantics "is associated or not" we can use a pointer to a map_view.
  742. ]]
  743. [*Matias]
  744. [:['
  745. OK, then you agree to that map_views stores a reference instead
  746. of a pointer?
  747. ]]
  748. [*Joaquin]
  749. [:['
  750. It depends on the semantics you want to give to map_views, and in
  751. concrete to the copy of map_views.
  752. ]]
  753. ``
  754. map_view x=...;
  755. map_view y=...;
  756. x=y;
  757. ``
  758. [:['
  759. What is supposed to do this last line?
  760. ]]
  761. [:['
  762. 1. Rebinding of x, that is to say, x points at the same container that y.
  763. ]]
  764. [:['
  765. 2. Copy of the underlying container.
  766. ]]
  767. [:['
  768. If you want to implement 1, you cannot use references internally.
  769. If you want to implement 2, it is almost the same to use a reference or a pointer.
  770. ]]
  771. [*Matias]
  772. [:['
  773. If I want that they behave exactly as std::maps then I must go for 2.
  774. But if I think they as "views" of something, I like 1. The question is complicated.
  775. I add another option:
  776. ]]
  777. [:['
  778. 3. Error: operator= is declare as private in boost::bimap::map_view std_container
  779. ]]
  780. [:['
  781. Also What happens with `std_container = view;`? and with `view = std_container;`?
  782. ]]
  783. [endsect]
  784. [endsect]