📄 rationale.qbk
字号:
[/licenseBoost.BimapCopyright (c) 2006-2007 Matias CapelettoDistributed under the Boost Software License, Version 1.0.(See accompanying file LICENSE_1_0.txt or copy athttp://www.boost.org/LICENSE_1_0.txt)][/ QuickBook Document version 1.4 ][section Rationale]This section assumes that you have read all other sections, the most ofimportant of which being ['tutorial], ['std::set theory] and the ['reference],and that you have tested the library. A lot of effort was invested inmaking the interface as intuitive and clean as possible. If youunderstand, and hopefully like, the interface of this library, it willbe a lot easier to read this rationale. The following section is littlemore than a rationale. This library was coded in the context of theGoogle SoC 2006 and the student and mentor were in different continents.A great deal of email flowed between Joaquin and Matias. The juiciestparts of the conversations where extracted and rearranged here.[note To browse the code, you can use the [@doxydoc/index.html ['Bimap Complete Reference]], adoxygen-powered document targeted at developers.][section General Design]The initial explanation includes few features. This section aims todescribe the general design of the library and excludes details of thosefeatures that are of lesser importance; these features will beintroduced later.The design of the library is divided into two parts. The first is theconstruction of a [^relation] class. This will be the object stored andmanaged by the [^multi_index_container] core. The idea is to make thisclass as easy as possible to use, while making it efficient in terms ofmemory and access time. This is a cornerstone in the design of[*Boost.Bimap] and, as you will see in this rationale, the rest of thedesign follows easily.The following interface is necessary for the [^relation] class: typedef -unspecified- TA; typedef -unspecified- TB; TA a, ai; TB b, bi; typedef relation< TA, TB > rel; STATIC_ASSERT( is_same< rel::left_type , TA >::value ); STATIC_ASSERT( is_same< rel::right_type, TB >::value ); rel r(ai,bi); assert( r.left == ai && r.right == bi ); r.left = a; r.right = b; assert( r.left == a && r.right == b ); typedef pair_type_by< member_at::left , rel >::type pba_type; STATIC_ASSERT( is_same< pba_type::first_type , TA >::value ); STATIC_ASSERT( is_same< pba_type::second_type, TB >::value ); typedef pair_type_by< member_at::right, rel >::type pbb_type; STATIC_ASSERT( is_same< pbb_type::first_type , TB >::value ); STATIC_ASSERT( is_same< pbb_type::second_type, TA >::value ); pba_type pba = pair_by< member_at::left >(r); assert( pba.first == r.left && pba.second == r.right ); pbb_type pbb = pair_by< member_at::right >(r); assert( pbb.first == r.right && pbb.second == r.left );__RELATION__Although this seems straightforward, as will be seen later, it is themost difficult code hack of the library. It is indeed very easy if werelax some of the efficiency constraints. For example, it is trivial ifwe allow a relation to have greater size than the the sum of those ofits components. It is equally simple if access speed is not important.One of the first decisions made about [*Boost.Bimap] was, however, that, inorder to be useful, it had to achieve zero overhead over the wrapped[*Boost.MultiIndex] container. Finally, there is another constraint thatcan be relaxed: conformance to C++ standards, but this is quiteunacceptable. Let us now suppose that we have coded this class, and itconforms to what was required.The second part is based on this [^relation] class. We can now view thedata in any of three ways: `pair<A,B>`, `relation<A,B>` and `pair<B,A>`.Suppose that our bimap supports only one-to-one relations. (Otherrelation types are considered additional features in this design.)The proposed interface is very simple, and it is based heavily on theconcepts of the STL. Given a `bimap<A,B> bm`:# `bm.left` is signature-compatible with a `std::map<A,B>`# `bm.right` is signature-compatible with a `std::map<B,A>`# `bm` is signature-compatible with a `std::set<relation<A,B> >`__SIMPLE_BIMAP__This interface is easily learned by users who have a STL background, aswell as being simple and powerful. This is the general design.[heading Relation Implementation]This section explains the details of the actual [^relation] class implementation.The first thing that we can imagine is the use of an [^union]. Regrettably,the current C++ standard only allows unions of POD types. For the views,we can try a wrapper around a `relation<A,B>` that has two referencesnamed first and second that bind to `A` and `B`, or to `B` and `A`. relation<TA,TB> r; const_reference_pair<A,B> pba(r); const_reference_pair<B,A> pbb(r);It is not difficult to code the relation class using this, but tworeferences are initialized at every access and using of `pba.first` willbe slower in most compilers than using `r.left` directly . There isanother hidden drawback of using this scheme: it is notiterator-friendly, since the map views iterators must be degraded to['Read Write] instead of ['LValue]. This will be explained later.At first, this seems to be the best we can do with the current C++standard. However there is a solution to this problem that does notconform very well to C++ standards but does achieve zero overhead interms of access time and memory, and additionally allows the viewiterators to be upgraded to ['LValue] again.In order to use this, the compiler must conform to alayout-compatibility clause that is not currently in the standard but isvery natural. The additional clause imposes that if we have two classes: struct class_a_b { Type1 name_a; Type2 name_b; }; struct class_b_a { Type1 name_b; Type2 name_a; };then the storage layout of [^class_a_b] is equal to the storage layout of[^class_b_a]. If you are surprised to learn that this does not hold in astandards-compliant C++ compiler, welcome to the club. It is the naturalway to implement it from the point of view of the compiler's vendor andis very useful for the developer. Maybe it will be included in thestandard some day. Every current compiler conforms to this.If we are able to count on this, then we can implement an idiom called[^mutant]. The idea is to provide a secure wrapper around [^reinterpret_cast].A class can declare that it can be viewed using different view classesthat are storage-compatible with it. Then we use the free function[^mutate<view>(mutant)] to get the view. The `mutate` function checks atcompile time that the requested view is declared in the mutant views list.We implement a class name `structured_pair` that is signature-compatiblewith a `std::pair`, while the storage layout is configured with a thirdtemplate parameter. Two instances of this template class will providethe views of the relation.The thing is that if we want to be standards-compliant, we cannot usethis approach. It is very annoying not to be able to use something thatwe know will work with every compiler and that is far better thanalternatives. So -- and this is an important decision -- we have to finda way to use it and still make the library standards-compliant.The idea is very simple. We code both approaches: theconst_reference_pair-based and the mutant-based, and use the mutantapproach if the compiler is compliant with our new layout-compatibleclause. If the compiler really messes things up, we degrade theperformance of the bimap a little. The only drawback here is that, whilethe mutant approach allows to make ['LValue] iterators, we have to degradethem to ['Read Write] in both cases, because we require that the same codebe compilable by any standards-compliant compiler.[noteTesting this approach in all the supported compilers indicated that themutant idiom was always supported. The strictly compliant version wasremoved from the code because it was never used.][heading Bimap Implementation]The core of bimap will be obviously a `multi_index_container`. The basicidea to tackle the implementation of the bimap class is to use[^iterator_adaptor] to convert the iterators from Boost.MultiIndex to the`std::map` and `std::set` behaviour. The `map_view` and the `set_view` can beimplemented directly using this new transformed iterators and a wrapperaround each index of the core container. However, there is a hiddenidiom here, that, once coded, will be very useful for other parts ofthis library and for Boost.MRU library. Following the ideas from`iterator_adaptor`, Boost.Bimap views are implemented using a[^container_adaptor]. There are several template classes (for example`map_adaptor` and `set_adaptor`) that take a `std::map` signature-conformantclass and new iterators, and adapt the container so it now uses thisiterators instead of the originals. For example, if you have a`std::set<int*>`, you can build other container that behaves exactly as a`std::set<int>` using `set_adaptor` and [^iterator_adaptor]. The combined useof this two tools is very powerful. A [^container_adaptor] can take classesthat do not fulfil all the requirements of the adapted container. Thenew container must define these missing functions.[endsect][section Additional Features][heading N-1, N-N, hashed maps]This is a very interesting point of the design. The framework introducedin ['std::set theory] permits the management of the different constraintswith a very simple and conceptual approach. It is easy both to rememberand to learn. The idea here is to allow the user to specify the collection typeof each key directly. In order to implement this feature, we have tosolve two problems:* The index types of the `multi_index_container` core now depends onthe collection type used for each key.* The map views now change their semantics according to the collection typechosen.Boost.Bimap relies heavily on Boost.MPL to implement all of themetaprogramming necessary to make this framework work. By default, ifthe user does not specify the kind of the set, a `std::set` type is used.__BIMAP_STRUCTURES__[heading Collection type of relation constraints]The constraints of the bimap set view are another very importantfeature. In general, Boost.Bimap users will base the set view type onone of the two collection types of their keys. It may be useful however to givethis set other constraints or simply to order it differently. Bydefault, Boost.Bimap bases the collection type of relations on the left collectiontype, but the user may choose between:* left_based* right_based* set_of_relation<>* multiset_of_relation<>* unordered_set_of_relation<>* unordered_multiset_of_relation<>* list_of* vector_ofIn the first two cases, there are only two indices in the`multi_index_core`, and for this reason, these are the preferred options.The implementation uses further metaprogramming to define a new index ifnecessary.[/[heading Hooking Data]This is one of the things that makes Boost.Bimap very appealing intackling a problem. In general, programmers use maps to accessinformation quickly. Boost.Bimap allows the user to hook data inside thebimap so that it is not necessary to maintain another map. Theimplementation is based heavily on metaprogramming.][heading Tagged]The idea of using tags instead of the [^member_at::side] idiom is veryappealing since code that uses it is more readable. The only cost iscompile time. ['boost/bimap/tagged] is the implementation of a non-invasivetagged idiom. The [^relation] class is built in such a way that even whenthe user uses tags, the [^member_at::side] idiom continues to work. This isgood since an user can start tagging even before completing the codingof the algorithm, and the untagged code continues to work. Thedevelopment becomes a little more complicated when user-defined tags areincluded, but there are many handy metafunctions defined in the [^tagged]idiom that help to keep things simple enough.__TAGGED__[endsect][section Code]You can browse the code using the [@doxydoc/index.html [*Boost.Bimap doxygen docs]].The code follows the [@http://www.boost.org/more/lib_guide.htm Boost Library Requirement and Guidelines] asclosely as possible.[table folders in boost/bimap[[name][what is inside?]][[/ ][user level header files ]][[tagged/ ][tagged idiom ]][[relation/ ][the bimap data ]][[container_adaptor/ ][easy way of adapting containers ]][[views/ ][bimap views ]][[property_map/ ][support for property map concept ]]][table folders in each folder[[name][what is inside?]][[ ][class definitions]]
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -