From b35bef74e475a3735d105d440bd0f5f6f2382c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joaqu=C3=ADn=20M=2E=20L=C3=B3pez=20Mu=C3=B1oz?= Date: Fri, 7 May 2004 10:44:23 +0000 Subject: [PATCH] initial commit [SVN r22759] --- .gitattributes | 96 ++ Jamfile | 16 + doc/acknowledgements.html | 124 ++ doc/advanced_topics.html | 1321 +++++++++++++++ doc/compiler_specifics.html | 314 ++++ doc/examples.html | 319 ++++ doc/future_work.html | 273 +++ doc/index.html | 74 + doc/lopez.jpg | Bin 0 -> 15167 bytes doc/multi_index_cont_example.png | Bin 0 -> 87417 bytes doc/next.gif | Bin 0 -> 852 bytes doc/perf_1o.png | Bin 0 -> 13186 bytes doc/perf_1o1s.png | Bin 0 -> 14190 bytes doc/perf_1s.png | Bin 0 -> 13256 bytes doc/perf_2o.png | Bin 0 -> 13043 bytes doc/perf_2o1s.png | Bin 0 -> 13406 bytes doc/perf_3o.png | Bin 0 -> 13258 bytes doc/performance.html | 724 ++++++++ doc/prev.gif | Bin 0 -> 852 bytes doc/reference/index.html | 116 ++ doc/reference/indices.html | 328 ++++ doc/reference/key_extraction.html | 1505 +++++++++++++++++ doc/reference/multi_index_container.html | 831 +++++++++ doc/reference/ord_indices.html | 909 ++++++++++ doc/reference/seq_indices.html | 837 +++++++++ doc/style.css | 48 + doc/tests.html | 146 ++ doc/tutorial.html | 1260 ++++++++++++++ doc/up.gif | Bin 0 -> 853 bytes example/Jamfile | 44 + example/basic.cpp | 122 ++ example/bimap.cpp | 142 ++ example/complex_structs.cpp | 315 ++++ example/composite_keys.cpp | 295 ++++ example/memfun_key.cpp | 76 + example/non_default_ctor.cpp | 96 ++ example/sequenced.cpp | 101 ++ include/boost/multi_index/composite_key.hpp | 948 +++++++++++ .../multi_index/detail/access_specifier.hpp | 39 + .../boost/multi_index/detail/allocator.hpp | 213 +++ .../boost/multi_index/detail/auto_space.hpp | 64 + .../boost/multi_index/detail/base_type.hpp | 81 + .../boost/multi_index/detail/converter.hpp | 48 + include/boost/multi_index/detail/copy_map.hpp | 127 ++ .../detail/def_ctor_tuple_cons.hpp | 55 + include/boost/multi_index/detail/has_tag.hpp | 38 + .../multi_index/detail/header_holder.hpp | 49 + .../boost/multi_index/detail/index_base.hpp | 133 ++ .../multi_index/detail/index_iterator.hpp | 140 ++ .../multi_index/detail/index_iterator_fwd.hpp | 40 + .../multi_index/detail/index_node_base.hpp | 39 + .../boost/multi_index/detail/index_proxy.hpp | 78 + .../multi_index/detail/invariant_assert.hpp | 17 + .../multi_index/detail/is_index_list.hpp | 36 + .../multi_index/detail/modify_key_adaptor.hpp | 45 + .../detail/msvc_index_specifier.hpp | 65 + .../multi_index/detail/no_duplicate_tags.hpp | 93 + .../boost/multi_index/detail/node_type.hpp | 73 + .../multi_index/detail/ord_index_args.hpp | 86 + .../multi_index/detail/ord_index_node.hpp | 457 +++++ .../multi_index/detail/ord_index_ops.hpp | 121 ++ .../boost/multi_index/detail/prevent_eti.hpp | 56 + .../boost/multi_index/detail/safe_mode.hpp | 339 ++++ .../boost/multi_index/detail/scope_guard.hpp | 270 +++ .../multi_index/detail/seq_index_node.hpp | 197 +++ .../multi_index/detail/seq_index_ops.hpp | 164 ++ .../boost/multi_index/detail/unbounded.hpp | 35 + .../multi_index/detail/value_compare.hpp | 48 + include/boost/multi_index/identity.hpp | 119 ++ include/boost/multi_index/identity_fwd.hpp | 22 + include/boost/multi_index/indexed_by.hpp | 68 + include/boost/multi_index/key_extractors.hpp | 17 + include/boost/multi_index/mem_fun.hpp | 171 ++ include/boost/multi_index/member.hpp | 228 +++ include/boost/multi_index/ordered_index.hpp | 1124 ++++++++++++ .../boost/multi_index/ordered_index_fwd.hpp | 112 ++ .../boost/multi_index/safe_mode_errors.hpp | 43 + include/boost/multi_index/sequenced_index.hpp | 751 ++++++++ .../boost/multi_index/sequenced_index_fwd.hpp | 87 + include/boost/multi_index/tag.hpp | 79 + include/boost/multi_index_container.hpp | 864 ++++++++++ include/boost/multi_index_container_fwd.hpp | 117 ++ index.html | 16 + perf/Jamfile | 15 + perf/test_perf.cpp | 556 ++++++ test/Jamfile | 41 + test/employee.hpp | 88 + test/pair_of_ints.hpp | 34 + test/pre_multi_index.hpp | 31 + test/test_all_main.cpp | 53 + test/test_basic.cpp | 90 + test/test_basic.hpp | 11 + test/test_basic_main.cpp | 18 + test/test_capacity.cpp | 50 + test/test_capacity.hpp | 11 + test/test_capacity_main.cpp | 19 + test/test_comparison.cpp | 84 + test/test_comparison.hpp | 11 + test/test_comparison_main.cpp | 19 + test/test_composite_key.cpp | 448 +++++ test/test_composite_key.hpp | 11 + test/test_composite_key_main.cpp | 18 + test/test_conv_iterators.cpp | 55 + test/test_conv_iterators.hpp | 12 + test/test_conv_iterators_main.cpp | 21 + test/test_copy_assignment.cpp | 91 + test/test_copy_assignment.hpp | 11 + test/test_copy_assignment_main.cpp | 18 + test/test_iterators.cpp | 87 + test/test_iterators.hpp | 11 + test/test_iterators_main.cpp | 19 + test/test_key_extractors.cpp | 215 +++ test/test_key_extractors.hpp | 11 + test/test_key_extractors_main.cpp | 18 + test/test_list_ops.cpp | 184 ++ test/test_list_ops.hpp | 11 + test/test_list_ops_main.cpp | 18 + test/test_modifiers.cpp | 131 ++ test/test_modifiers.hpp | 11 + test/test_modifiers_main.cpp | 20 + test/test_mpl_ops.cpp | 75 + test/test_mpl_ops.hpp | 11 + test/test_mpl_ops_main.cpp | 18 + test/test_projection.cpp | 109 ++ test/test_projection.hpp | 11 + test/test_projection_main.cpp | 20 + test/test_range.cpp | 118 ++ test/test_range.hpp | 11 + test/test_range_main.cpp | 18 + test/test_safe_mode.cpp | 195 +++ test/test_safe_mode.hpp | 11 + test/test_safe_mode_main.cpp | 19 + test/test_set_ops.cpp | 50 + test/test_set_ops.hpp | 11 + test/test_set_ops_main.cpp | 18 + test/test_special_list_ops.cpp | 83 + test/test_special_list_ops.hpp | 11 + test/test_special_list_ops_main.cpp | 18 + test/test_special_set_ops.cpp | 59 + test/test_special_set_ops.hpp | 11 + test/test_special_set_ops_main.cpp | 18 + test/test_update.cpp | 86 + test/test_update.hpp | 11 + test/test_update_main.cpp | 18 + 144 files changed, 22006 insertions(+) create mode 100644 .gitattributes create mode 100644 Jamfile create mode 100644 doc/acknowledgements.html create mode 100644 doc/advanced_topics.html create mode 100644 doc/compiler_specifics.html create mode 100644 doc/examples.html create mode 100644 doc/future_work.html create mode 100644 doc/index.html create mode 100644 doc/lopez.jpg create mode 100644 doc/multi_index_cont_example.png create mode 100644 doc/next.gif create mode 100644 doc/perf_1o.png create mode 100644 doc/perf_1o1s.png create mode 100644 doc/perf_1s.png create mode 100644 doc/perf_2o.png create mode 100644 doc/perf_2o1s.png create mode 100644 doc/perf_3o.png create mode 100644 doc/performance.html create mode 100644 doc/prev.gif create mode 100644 doc/reference/index.html create mode 100644 doc/reference/indices.html create mode 100644 doc/reference/key_extraction.html create mode 100644 doc/reference/multi_index_container.html create mode 100644 doc/reference/ord_indices.html create mode 100644 doc/reference/seq_indices.html create mode 100644 doc/style.css create mode 100644 doc/tests.html create mode 100644 doc/tutorial.html create mode 100644 doc/up.gif create mode 100644 example/Jamfile create mode 100644 example/basic.cpp create mode 100644 example/bimap.cpp create mode 100644 example/complex_structs.cpp create mode 100644 example/composite_keys.cpp create mode 100644 example/memfun_key.cpp create mode 100644 example/non_default_ctor.cpp create mode 100644 example/sequenced.cpp create mode 100644 include/boost/multi_index/composite_key.hpp create mode 100644 include/boost/multi_index/detail/access_specifier.hpp create mode 100644 include/boost/multi_index/detail/allocator.hpp create mode 100644 include/boost/multi_index/detail/auto_space.hpp create mode 100644 include/boost/multi_index/detail/base_type.hpp create mode 100644 include/boost/multi_index/detail/converter.hpp create mode 100644 include/boost/multi_index/detail/copy_map.hpp create mode 100644 include/boost/multi_index/detail/def_ctor_tuple_cons.hpp create mode 100644 include/boost/multi_index/detail/has_tag.hpp create mode 100644 include/boost/multi_index/detail/header_holder.hpp create mode 100644 include/boost/multi_index/detail/index_base.hpp create mode 100644 include/boost/multi_index/detail/index_iterator.hpp create mode 100644 include/boost/multi_index/detail/index_iterator_fwd.hpp create mode 100644 include/boost/multi_index/detail/index_node_base.hpp create mode 100644 include/boost/multi_index/detail/index_proxy.hpp create mode 100644 include/boost/multi_index/detail/invariant_assert.hpp create mode 100644 include/boost/multi_index/detail/is_index_list.hpp create mode 100644 include/boost/multi_index/detail/modify_key_adaptor.hpp create mode 100644 include/boost/multi_index/detail/msvc_index_specifier.hpp create mode 100644 include/boost/multi_index/detail/no_duplicate_tags.hpp create mode 100644 include/boost/multi_index/detail/node_type.hpp create mode 100644 include/boost/multi_index/detail/ord_index_args.hpp create mode 100644 include/boost/multi_index/detail/ord_index_node.hpp create mode 100644 include/boost/multi_index/detail/ord_index_ops.hpp create mode 100644 include/boost/multi_index/detail/prevent_eti.hpp create mode 100644 include/boost/multi_index/detail/safe_mode.hpp create mode 100644 include/boost/multi_index/detail/scope_guard.hpp create mode 100644 include/boost/multi_index/detail/seq_index_node.hpp create mode 100644 include/boost/multi_index/detail/seq_index_ops.hpp create mode 100644 include/boost/multi_index/detail/unbounded.hpp create mode 100644 include/boost/multi_index/detail/value_compare.hpp create mode 100644 include/boost/multi_index/identity.hpp create mode 100644 include/boost/multi_index/identity_fwd.hpp create mode 100644 include/boost/multi_index/indexed_by.hpp create mode 100644 include/boost/multi_index/key_extractors.hpp create mode 100644 include/boost/multi_index/mem_fun.hpp create mode 100644 include/boost/multi_index/member.hpp create mode 100644 include/boost/multi_index/ordered_index.hpp create mode 100644 include/boost/multi_index/ordered_index_fwd.hpp create mode 100644 include/boost/multi_index/safe_mode_errors.hpp create mode 100644 include/boost/multi_index/sequenced_index.hpp create mode 100644 include/boost/multi_index/sequenced_index_fwd.hpp create mode 100644 include/boost/multi_index/tag.hpp create mode 100644 include/boost/multi_index_container.hpp create mode 100644 include/boost/multi_index_container_fwd.hpp create mode 100644 index.html create mode 100644 perf/Jamfile create mode 100644 perf/test_perf.cpp create mode 100644 test/Jamfile create mode 100644 test/employee.hpp create mode 100644 test/pair_of_ints.hpp create mode 100644 test/pre_multi_index.hpp create mode 100644 test/test_all_main.cpp create mode 100644 test/test_basic.cpp create mode 100644 test/test_basic.hpp create mode 100644 test/test_basic_main.cpp create mode 100644 test/test_capacity.cpp create mode 100644 test/test_capacity.hpp create mode 100644 test/test_capacity_main.cpp create mode 100644 test/test_comparison.cpp create mode 100644 test/test_comparison.hpp create mode 100644 test/test_comparison_main.cpp create mode 100644 test/test_composite_key.cpp create mode 100644 test/test_composite_key.hpp create mode 100644 test/test_composite_key_main.cpp create mode 100644 test/test_conv_iterators.cpp create mode 100644 test/test_conv_iterators.hpp create mode 100644 test/test_conv_iterators_main.cpp create mode 100644 test/test_copy_assignment.cpp create mode 100644 test/test_copy_assignment.hpp create mode 100644 test/test_copy_assignment_main.cpp create mode 100644 test/test_iterators.cpp create mode 100644 test/test_iterators.hpp create mode 100644 test/test_iterators_main.cpp create mode 100644 test/test_key_extractors.cpp create mode 100644 test/test_key_extractors.hpp create mode 100644 test/test_key_extractors_main.cpp create mode 100644 test/test_list_ops.cpp create mode 100644 test/test_list_ops.hpp create mode 100644 test/test_list_ops_main.cpp create mode 100644 test/test_modifiers.cpp create mode 100644 test/test_modifiers.hpp create mode 100644 test/test_modifiers_main.cpp create mode 100644 test/test_mpl_ops.cpp create mode 100644 test/test_mpl_ops.hpp create mode 100644 test/test_mpl_ops_main.cpp create mode 100644 test/test_projection.cpp create mode 100644 test/test_projection.hpp create mode 100644 test/test_projection_main.cpp create mode 100644 test/test_range.cpp create mode 100644 test/test_range.hpp create mode 100644 test/test_range_main.cpp create mode 100644 test/test_safe_mode.cpp create mode 100644 test/test_safe_mode.hpp create mode 100644 test/test_safe_mode_main.cpp create mode 100644 test/test_set_ops.cpp create mode 100644 test/test_set_ops.hpp create mode 100644 test/test_set_ops_main.cpp create mode 100644 test/test_special_list_ops.cpp create mode 100644 test/test_special_list_ops.hpp create mode 100644 test/test_special_list_ops_main.cpp create mode 100644 test/test_special_set_ops.cpp create mode 100644 test/test_special_set_ops.hpp create mode 100644 test/test_special_set_ops_main.cpp create mode 100644 test/test_update.cpp create mode 100644 test/test_update.hpp create mode 100644 test/test_update_main.cpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3e84d7c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/Jamfile b/Jamfile new file mode 100644 index 0000000..b7f7391 --- /dev/null +++ b/Jamfile @@ -0,0 +1,16 @@ +# Boost.MultiIndex examples and tests Jamfile +# +# Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and distribution +# are subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/libs/multi_index for library home page. + + +subproject libs/multi_index ; + +# please order by name to ease maintenance + +subinclude libs/multi_index/example ; +subinclude libs/multi_index/test ; +subinclude libs/multi_index/perf ; diff --git a/doc/acknowledgements.html b/doc/acknowledgements.html new file mode 100644 index 0000000..df57256 --- /dev/null +++ b/doc/acknowledgements.html @@ -0,0 +1,124 @@ + + + + + +Boost.MultiIndex Documentation - Acknowledgements + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Acknowledgements

+ + + +
+ +
+ +

+Fernando Cacciola, Darren Cook, Beman Dawes, Jeremy Maitin-Shepard and Daryle +Walker from the Boost mailing list provided useful suggestions for improvement +on the first alpha releases of the library. Gang Wang discovered several +bugs in the code. Thomas Wenisch brought out the idea of "sequence sets" +from which sequenced indices were designed. Giovanni Bajo, Chris Little and +Maxim Yegorushkin tested the library on several platforms. Rosa +Bernárdez proofread the last version of the tutorial. +

+ +

+Pavel Voženílek has been immensely helpful in thoroughly reviewing +every single bit of the library, and he also suggested several extra +functionalities, most notably range querying, safe mode, polymorphic key +extractors and MPL support. Thank you! +

+ +

+The Boost acceptance review took place between March 20th and 30th 2004. +Pavel Voženílek was the review manager. Thanks to all the people +who participated and specially to those who submitted reviews: +Fredrik Blomqvist, Tom Brinkman, Paul A Bristow, Darren Cook, Jeff Garland, +David B. Held, Brian McNamara, Gary Powell, Rob Stewart, Arkadiy Vertleyb, +Jörg Walter. Other Boost members also contributed ideas, particularly +in connection with the library's naming scheme: Pavol Droba, +Dave Gomboc, Jeremy Maitin-Shepard, Thorsten Ottosen, Matthew Vogt, +Daryle Walker. My apologies if I inadvertently left somebody out of this +list. +

+ +

+Boost.MultiIndex could not have been written without Aleksey Gurtovoy +et al. superb Boost MPL +Library. Also, Aleksey's techniques for dealing with ETI-related +problems in MSVC++ 6.0 helped solve some internal issues of the library. +

+ +

+The internal implementation of red-black trees is based on that of SGI STL +stl_tree.h file: +

+ +
+Copyright (c) 1996,1997 +Silicon Graphics Computer Systems, Inc. +
+Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation. Silicon Graphics makes no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +
+
+Copyright (c) 1994 +Hewlett-Packard Company +
+Permission to use, copy, modify, distribute and sell this software +and its documentation for any purpose is hereby granted without fee, +provided that the above copyright notice appear in all copies and +that both that copyright notice and this permission notice appear +in supporting documentation. Hewlett-Packard Company makes no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +
+ +

+ +I would like to dedicate this piece of work to Rosa Bernárdez, my very first +C++ teacher, for her unconditional support in many endeavors of which programming is +by no means the most important. In memory of my cat López (2001-2003): he +lived too fast, died too young. +
+

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/advanced_topics.html b/doc/advanced_topics.html new file mode 100644 index 0000000..9435649 --- /dev/null +++ b/doc/advanced_topics.html @@ -0,0 +1,1321 @@ + + + + + +Boost.MultiIndex Documentation - Advanced topics + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Advanced topics

+ + + +
+ +
+ +

Contents

+ + + +

Composite keys

+ +

+In relational databases, composite keys depend on two or more fields of a given table. +The analogous concept in Boost.MultiIndex is modeled by means of + +composite_key, as shown in the example: +

+ +
+struct phonebook_entry
+{
+  std::string family_name;
+  std::string given_name;
+  std::string phone_number;
+
+  phonebook_entry(
+    std::string family_name,
+    std::string given_name,
+    std::string phone_number):
+    family_name(family_name),given_name(given_name),phone_number(phone_number)
+  {}
+};
+
+// define a multi_index_container with a composite key on
+// (family_name,given_name)
+typedef multi_index_container<
+  phonebook_entry,
+  indexed_by<
+    //non-unique as some subscribers might have more than one number
+    ordered_non_unique< 
+      composite_key<
+        phonebook_entry,
+        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
+        member<phonebook_entry,std::string,&phonebook_entry::given_name>
+      >
+    >,
+    ordered_unique< // unique as numbers belong to only one subscriber
+      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
+    >
+  >
+> phonebook;
+
+ +

+composite_key accepts two or more key extractors on the same +value (here, phonebook_entry). Lookup operations on a composite +key are accomplished by passing tuples with the values searched: +

+ +
+phonebook pb;
+...
+// search for Dorothea White's number
+phonebook::iterator it=pb.find(
+  boost::make_tuple(std::string("White"),std::string("Dorothea")));
+std::string number=it->phone_number;
+
+ +

+Composite keys are sorted by lexicographical order, i.e. sorting is performed +by the first key, then the second key if the first one is equal, etc. This +order allows for partial searches where only the first keys are specified: +

+ +
+phonebook pb;
+...
+// look for all Whites
+std::pair<phonebook::iterator,phonebook::iterator> p=
+  pb.equal_range(boost::make_tuple(std::string("White")));
+
+ +

+On the other hand, partial searches without specifying the first keys are not +allowed. +

+ +

+By default, the corresponding std::less predicate is used +for each subkey of a composite key. Alternate comparison predicates can +be specified with +composite_key_compare: +

+ +
+// phonebook with given names in reverse order
+
+typedef multi_index_container<
+  phonebook_entry,
+  indexed_by<
+    ordered_non_unique<
+      composite_key<
+        phonebook_entry,
+        member<phonebook_entry,std::string,&phonebook_entry::family_name>,
+        member<phonebook_entry,std::string,&phonebook_entry::given_name>
+      >,
+      composite_key_compare<
+        std::less<std::string>,   // family names sorted as by default
+        std::greater<std::string> // given names reversed
+      >
+    >,
+    ordered_unique<
+      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
+    >
+  >
+> phonebook;
+
+ +

+See Example 7 in the examples section +for an application of composite_key. +

+ +

Advanced features of Boost.MultiIndex key +extractors

+ +

+The Key Extractor +concept allows the same object to extract keys from several different types, +possibly through suitably defined overloads of operator(): +

+ +
+// example of a name extractor from employee and employee *
+struct name_extractor
+{
+  const std::string& operator()(const employee& e)const{return e.name;}
+  std::string&       operator()(employee& e)const{return e.name;}
+  std::string&       operator()(employee* e)const{return e->name;}
+};
+
+ +

+This possibility is fully exploited by predefined key extractors provided +by Boost.MultiIndex, making it simpler to define multi_index_containers +where elements are pointers or references to the actual objects. The following +specifies a multi_index_container of pointers to employees sorted by their +names. +

+ +
+typedef multi_index_container<
+  employee *,
+  indexed_by<
+    ordered_non_unique<member<employee,std::string,&employee::name> > >
+> employee_set;
+
+ +

+Note that this is specified in exactly the same manner as a multi_index_container +of actual employee objects: member takes care of the +extra dereferencing needed to gain access to employee::name. A similar +functionality is provided for interoperability with reference wrappers from +Boost.Ref: +

+ +
+typedef multi_index_container<
+  boost::reference_wrapper<const employee>,
+  indexed_by<
+    ordered_non_unique<member<employee,std::string,&employee::name> > >
+> employee_set;
+
+ +

+In fact, support for pointers is further extended to accept what we call +chained pointers. Such a chained pointer is defined by induction as a raw or +smart pointer or iterator to the actual element, to a reference wrapper of the +element or to another chained pointer; that is, chained pointers are arbitrary +compositions of pointer-like types ultimately dereferencing +to the element from where the key is to be extracted. Examples of chained +pointers to employee are: +

+In general, chained pointers with dereferencing distance greater than 1 are not +likely to be used in a normal program, but they can arise in frameworks +which construct "views" as multi_index_containers from preexisting +multi_index_containers. +

+ +

+In order to present a short summary of the different usages of Boost.MultiIndex +key extractors in the presence of reference wrappers and pointers, consider the +following final type: +

+ +
+struct T
+{
+  int       i;
+  const int j;
+  int       f()const;
+  int       g();
+};
+
+ +

+The table below lists the appropriate key extractors to be used for +different pointer and reference wrapper types based on T, for +each of its members. +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Use cases for Boost.MultiIndex key extractors.
element typesorted bykey extractorapplicable to
const elements?
read/write?
Timember<T,int,&T::i>yesyes
jmember<T,const int,&T::j>yesno
f()const_mem_fun<T,int,&T::f>yesno
g()mem_fun<T,int,&T::g>nono
reference_wrapper<T>imember<T,int,&T::i>yesyes
jmember<T,const int,&T::j>yesno
f()const_mem_fun<T,int,&T::f>yesno
g()mem_fun<T,int,&T::g>yesno
reference_wrapper<const T>imember<T,const int,&T::i>yesno
jmember<T,const int,&T::j>yesno
f()const_mem_fun<T,int,&T::f>yesno
g() 
chained pointer to T
+ or to reference_wrapper<T>
imember<T,int,&T::i>yesyes
jmember<T,const int,&T::j>yesno
f()const_mem_fun<T,int,&T::f>yesno
g()mem_fun<T,int,&T::g>yesno
chained pointer to const T
+ or to reference_wrapper<const T>
imember<T,const int,&T::i>yesno
jmember<T,const int,&T::j>yesno
f()const_mem_fun<T,int,&T::f>yesno
g() 
+

+ +

+The column "applicable to const elements?" states whether the +corresponding key extractor can be used when passed constant elements (this +relates to the elements specified in the first column, not the referenced +T objects). The only negative case is for T::g when +the elements are raw T objects, which make sense as we are dealing +with a non-constant member function: this also implies that multi_index_containers +of elements of T cannot be sorted by T::g, because +elements contained within a multi_index_container are treated as constant. +

+ +

+A key extractor is called read/write if it returns a non-constant reference +to the key when passed a non-constant element, and it is called read-only +otherwise. In order to use multi_index_container::modify_key, the associated +key extractor must be read/write. The column "read/write?" shows that most +combinations yield read-only extractors. +

+ +

+Some care has to be taken to preserve const-correctness in the +specification of the key extractors: in some sense, the const +qualifier is carried along to the member part, even if that particular +member is not defined as const. For instance, if the elements +are of type const T *, sorting by T::i is not +specified as member<const T,int,&T::i>, but rather as +member<T,const int,&T::i>. +

+ +

+For practical demonstrations of use of these key extractors, refer to +example 2 and +example 6 in the examples section. +

+ +

Use of member_offset

+ +

+The member key extractor poses some problems in compilers +that do not properly support pointers to members as non-type +template arguments. The following compilers have been confirmed not +to work correctly with member: +

+In these cases, a replacement utility +member_offset +has been provided that does the work of member at the +expense of less convenient notation and the possibility of +non-conformance with the standard. Please consult +the reference for further information on member_offset. +

+ +

+The following test program can help determine if your compiler +properly supports pointers to members as non-type template parameters: +

+ +
+#include <boost/multi_index_container/member.hpp>
+#include <utility>
+#include <iostream>
+
+using namespace std;
+using boost::multi_index::member;
+
+typedef std::pair<int,int> pair_of_ints;
+
+int main()
+{
+  pair_of_ints p(0,1);
+  int i=member<pair_of_ints,int,&pair_of_ints::first>()(p);
+  int j=member<pair_of_ints,int,&pair_of_ints::second>()(p);
+  if(i!=0||j!=1){
+    cout<<"WARNING: compiler does not properly support\n"
+          "pointers as non-type template parameters"<<endl;
+    return 1;
+  }
+
+  cout<<"test succesful"<<endl;
+  return 0;
+}
+
+
+ +

+If you find a compiler not listed here that does not pass the test, +please report to the maintainer of the library. As an example of use, +given the class

+ +
+class A
+{
+  int x;
+}
+
+ +

+the instantiation member<A,int,&A::x> can be simulated then +as member_offset<A,int,offsetof(A,x)>. +

+ +

+For those writing portable code, Boost.MultiIndex provides the ternary macro +BOOST_MULTI_INDEX_MEMBER. Continuing with the example above, the +expression +

+ +
+BOOST_MULTI_INDEX_MEMBER(A,int,x)
+
+ +

+expands to either +

+ +
+member<A,int,&A::x>
+
+ +

+or alternatively to +

+ +
+member_offset<A,int,offsetof(A,x)>
+
+ +

+depending on whether the current compiler supports pointer to members as +non-type template arguments or not. The alternative expansion is driven by +the defect macro BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS, +which has been proposed for inclusion in the +Boost Configuration Library. +Until the defect macro is accepted, Boost.MultiIndex treats it as if defined for +

+If you detect this defect in a compiler other than these, you can take +advantage of BOOST_MULTI_INDEX_MEMBER by manually defining +BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS prior to the +inclusion of Boost.MultiIndex headers. +

+ +

Use of const_mem_fun_explicit and +mem_fun_explicit

+ +

+MSVC++ 6.0 has problems with const member functions as non-type +template parameters, and thus does not accept the const_mem_fun +key extractor. A simple workaround, fortunately, has been found, consisting +in specifying the type of these pointers as an additional template +parameter. The alternative const_mem_fun_explicit extractor +adopts this solution; for instance, given the type +

+ +
+struct A
+{
+  int f()const;
+};
+
+ +

+the extractor const_mem_fun<A,int,&A::f> can be replaced by +const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>. A similar +mem_fun_explicit class template is provided for non-constant +member functions. +

+ +

+If you are writing cross-platform code, the selection of either key extractor +is transparently handled by the macro BOOST_MULTI_INDEX_CONST_MEM_FUN, +so that +

+ +
+BOOST_MULTI_INDEX_CONST_MEM_FUN(A,int,f)
+
+ +

+expands by default to +

+ +
+const_mem_fun<A,int,&A::f>
+
+ +

+but resolves to +

+ +
+const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>
+
+ +

+in MSVC++ 6.0. An analogous macro BOOST_MULTI_INDEX_MEM_FUN is +provided as well. +

+ +

composite_key in compilers +without partial template specialization

+ +

+Much of the power of composite_key derives from the ability +to perform searches when only the first elements of the compound key are +given. In order to enable this functionality, std::less and +std::greater are specialized for + +composite_key_result instantiations to provide +overloads accepting tuples of values. +

+ +

+In those compilers that do not support partial template specialization, +tuple-based comparisons are not available by default. In this case, +multi_index_container instantiations using composite keys +will work as expected (elements are sorted lexicographically on the +results of the combined keys), except that lookup operations will not +accept tuples as an argument. The most obvious workaround +to this deficiency involves explicitly specifying the comparison +predicate with composite_key_compare: this is tedious as +the comparison predicates for all the element key extractors must be +explicitly typed. For this reason, Boost.MultiIndex provides the replacement +class template + +composite_key_result_less, that +acts as the missing specialization of std::less for +composite_key_results: +

+ +
+typedef composite_key<
+  phonebook_entry,
+  member<phonebook_entry,std::string,&phonebook_entry::family_name>,
+  member<phonebook_entry,std::string,&phonebook_entry::given_name>
+> ckey_t;
+
+typedef multi_index_container<
+  phonebook_entry,
+  indexed_by<
+    ordered_non_unique< 
+      ckey_t,
+      // composite_key_result_less plays the role of
+      // std::less<ckey_t::result_type>
+      composite_key_result_less<ckey_t::result_type>
+    >,
+    ordered_unique<
+      member<phonebook_entry,std::string,&phonebook_entry::phone_number>
+    >
+  >
+> phonebook;
+
+ +

+There is also an analogous + +composite_key_result_greater class to substitute for +specializations of std::greater. +

+ +

Use of ctor_args_list

+ +

+Although in most cases multi_index_containers will be default constructed +(or copied from a preexisting multi_index_container), sometimes it is +necessary to specify particular values for the internal objects used (key extractors, +comparison predicates, allocator), for instance if some of these objects do not have +a default constructor. The same situation can arise with standard STL containers, +which allow for the optional specification of such objects: +

+ +
+// example of non-default constructed std::set
+template<typename IntegralType>
+struct modulo_less
+{
+  modulo_less(IntegralType m):modulo(m){}
+
+  bool operator()(IntegralType x,IntegralType y)const
+  {
+    return (x%modulo)<(y%modulo);
+  }
+
+private:
+  IntegralType modulo;
+};
+
+typedef std::set<unsigned int,modulo_less<unsigned int> > modulo_set;
+
+modulo_set m(modulo_less<unsigned int>(10));
+
+ +

+multi_index_container does also provide this functionality, though in a +considerably more complex fashion, due to the fact that the constructor +of a multi_index_container has to accept values for all the internal +objects of its indices. The full form of multi_index_container constructor +is +

+ +
+explicit multi_index_container(
+    const ctor_args_list& args_list=ctor_args_list(),
+    const allocator_type& al=allocator_type());
+
+ +

+The specification of the allocator object poses no particular problems; +as for the ctor_args_list, this object is designed so as to hold +the necessary construction values for every index in the multi_index_container. +From the point of view of the user, ctor_args_list is equivalent +to the type +

+ +
+boost::tuple<C0,...,CI-1>
+
+ +

+where I is the number of indices, and Ci is +

+ +
+nth_index<i>::type::ctor_args
+
+ +

+that is, the nested type ctor_args of the i-th index. Each +ctor_args type is in turn a tuple holding values for constructor +arguments of the associated index: so, ordered indices demand a key extractor object +and a comparison predicate, while sequenced indices do not need any construction +argument. For instance, given the definition +

+ +
+typedef multi_index_container<
+  unsigned int,
+  indexed_by<
+    ordered_unique<identity<unsigned int> >,
+    ordered_non_unique<identity<unsigned int>, modulo_less<unsigned int> >,
+    sequenced<>
+  >
+> modulo_indexed_set;
+
+ +

+the corresponding ctor_args_list type is equivalent to +

+ +
+boost::tuple<
+  // ctr_args of index #0
+  boost::tuple<identity<unsigned int>,std::less<unsigned int> >,
+    
+  // ctr_args of index #1
+  boost::tuple<identity<unsigned int>,modulo_less<unsigned int> >,
+  
+  // sequenced indices do not have any construction argument
+  boost::tuple<>
+>
+
+ +

+Such a modulo_indexed_set cannot be default constructed, because +modulo_less does not provide a default constructor. The following shows +how the construction can be done: +

+ +
+modulo_indexed_set::ctor_args_list args_list=
+  boost::make_tuple(
+    // ctor_args for index #0 is default constructible
+    modulo_indexed_set::nth_index<0>::type::ctor_args(),
+    
+    boost::make_tuple(identity<unsigned int>(),modulo_less<unsigned int>(10)),
+    
+    // this is also default constructible  (actually, an empty tuple) 
+    modulo_indexed_set::nth_index<2>::type::ctor_args(),
+  );
+
+modulo_indexed_set m(args_list);
+
+ +

+A program is provided in the examples section that +puts in practise these concepts. +

+ +

Debugging support

+ +

+The concept of Design by Contract, originally developed as part +of Bertrand Meyer's Eiffel language, +revolves around the formulation of a contract between the user +of a library and the implementor, by which the first is required to +respect some preconditions on the values passed when invoking +methods of the library, and the implementor guarantees in return +that certain constraints on the results are met (postconditions), +as well as the honoring of specified internal consistency rules, called +invariants. Eiffel natively supports the three parts of the +contract just described by means of constructs require, +ensure and invariant, respectively. +

+ +

+C++ does not enjoy direct support for Design by Contract techniques: these +are customarily implemented as assertion code, often turned off in +release mode for performance reasons. Following this approach, +Boost.MultiIndex provides two distinct debugging modes: +

+These two modes are independent of each other and can be set on or off +individually. It is important to note that errors detected by safe mode are +due in principle to faulty code in the user's program, while +invariant-checking mode detects potential internal bugs in the +implementation of Boost.MultiIndex. +

+ +

Safe mode

+ +

+The idea of adding precondition checking facilities to STL as a debugging aid +was first introduced by Cay S. Horstmann in his +Safe STL library and later +adopted by STLport Debug +Mode. Similarly, Boost.MultiIndex features the so-called safe mode +in which all sorts of preconditions are checked when dealing with iterators +and functions of the library. +

+ +

+Boost.MultiIndex safe mode is set by globally defining the macro +BOOST_MULTI_INDEX_ENABLE_SAFE_MODE. Error conditions +are checked via the macro BOOST_MULTI_INDEX_SAFE_MODE_ASSERT, which +by default resolves to a call to +BOOST_ASSERT. +

+ +

+If the user decides to define her own version of +BOOST_MULTI_INDEX_SAFE_MODE_ASSERT, it has to take the form +

+ +
+BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code)
+
+ +

+where expr is the condition checked and error_code +is one value of the safe_mode::error_code enumeration: +

+ +
+namespace boost{
+
+namespace multi_index{
+
+namespace safe_mode{
+
+enum error_code
+{
+  invalid_iterator,             // default initialized iterator
+  not_dereferenceable_iterator, // iterator is not dereferenceable
+  not_incrementable_iterator,   // iterator points to end of sequence
+  not_decrementable_iterator,   // iterator points to beginning of sequence 
+  not_owner,                    // iterator does not belong to the container
+  not_same_owner,               // iterators belong to different containers
+  invalid_range,                // last not reachable from first
+  inside_range,                 // iterator lies within a range (and it mustn't)
+  same_container                // containers ought to be different
+};
+
+} // namespace multi_index::safe_mode
+
+} // namespace multi_index
+
+} // namespace boost
+
+ +

+For instance, the following replacement of +BOOST_MULTI_INDEX_SAFE_MODE_ASSERT throws an exception instead of +asserting: +

+ +
+#include <boost/multi_index_container/safe_mode_errors.hpp>
+
+struct safe_mode_exception
+{
+  safe_mode_exception(boost::multi_index::safe_mode::error_code error_code):
+    error_code(error_code)
+  {}
+
+  boost::multi_index::safe_mode::error_code error_code;
+};
+
+#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) \
+if(!(expr)){throw safe_mode_exception(error_code);}
+
+// This has to go before the inclusion of any header from Boost.MultiIndex,
+// except possibly safe_error_codes.hpp.
+
+ +

+Other possibilites, like outputting to a log or firing some kind of alert, are +also implementable. +

+ +

+Warning: Safe mode adds a very important overhead to the program +both in terms of space and time used, so in general it should not be set for +NDEBUG builds. Also, this mode is intended solely as a debugging aid, +and programs must not rely on it as part of their normal execution flow: in +particular, no guarantee is made that all possible precondition errors are diagnosed, +or that the checks remain stable across different versions of the library. +

+ +

Invariant-checking mode

+ +

+The so called invariant-checking mode of Boost.MultiIndex can be +set by globally defining the macro +BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING. +When this mode is in effect, all public functions of Boost.MultiIndex +will perform post-execution tests aimed at ensuring that the basic +internal invariants of the data structures managed are preserved. +

+ +

+If an invariant test fails, Boost.MultiIndex will indicate the failure +by means of the unary macro BOOST_MULTI_INDEX_INVARIANT_ASSERT. +Unless the user provides a definition for this macro, it defaults to + +BOOST_ASSERT. Any assertion of this kind should +be regarded in principle as a bug in the library. Please report such +problems, along with as much contextual information as possible, to the +maintainer of the library. +

+ +

+It is recommended that users of Boost.MultiIndex always set the +invariant-checking mode in debug builds. +

+ +

Simulating standard containers with + multi_index_container

+ +

Simulation of associative +containers

+ +

+Academic movitations aside, there is a practical interest in simulating standard +associative containers by means of multi_index_container, namely to take +advantage of extended functionalities provided by multi_index_container for +lookup, range querying and updating. +

+ +

+In order to simulate a std::set one can follow the substitution +rule: +

+ +
+std::set<Key,Compare,Allocator> ->
+  multi_index_container<
+    Key,
+    indexed_by<ordered_unique<identity<Key>,Compare> >,
+    Allocator
+  >
+
+ +

+In the default case where Compare=std::less<Key> and +Allocator=std::allocator<Key>, the substitution rule is +simplified as +

+ +
+std::set<Key> -> multi_index_container<Key>
+
+ +

+The substitution of multi_index_container for std::set keeps +the whole set of functionality provided by std::set, so in +principle it is a drop-in replacement needing no further adjustments. +

+ +

+std::multiset can be simulated in a similar manner, according to the +following rule: +

+ +
+std::multiset<Key,Compare,Allocator> ->
+  multi_index_container<
+    Key,
+    indexed_by<ordered_non_unique<identity<Key>,Compare> >,
+    Allocator
+  >
+
+ +

+When default values are taken into consideration, the rule takes the form +

+ +
+std::multiset<Key> ->
+  multi_index_container<
+    Key,
+    indexed_by<ordered_non_unique<identity<Key> > >
+  >
+
+ +

+The simulation of std::multisets with multi_index_container +results in a slight difference with respect to the interface offered: the member +function insert(const value_type&) does not return an +iterator as in std::multisets, but rather a +std::pair<iterator,bool> in the spirit of std::sets. +In this particular case, however, the bool member of the returned +pair is always true. +

+ +

+The case of std::maps and std::multimaps does not lend +itself to such a direct simulation by means of multi_index_container. The main +problem lies in the fact that elements of a multi_index_container are treated +as constant, while the std::map and std::multimap handle +objects of type std::pair<const Key,T>, thus allowing for free +modification of the value part. To overcome this difficulty we need to create an ad +hoc pair class: +

+ +
+template <typename T1,typename T2>
+struct mutable_pair
+{
+  typedef T1 first_type;
+  typedef T2 second_type;
+
+  mutable_pair():first(T1()),second(T2()){}
+  mutable_pair(const T1& f,const T2& s):first(f),second(s){}
+  mutable_pair(const std::pair<T1,T2>& p):first(p.first),second(p.second){}
+
+  T1         first;
+  mutable T2 second;
+};
+
+ +

+and so the substitution rules are: +

+ +
+std::map<Key,T,Compare,Allocator> ->
+  multi_index_container<
+    Element,
+    indexed_by<
+      ordered_unique<member<Element,Key,&Element::first>,Compare>
+    >,
+    typename Allocator::template rebind<Element>::other
+  >
+
+std::multimap<Key,T,Compare,Allocator> ->
+  multi_index_container<
+    Element,
+    indexed_by<
+      ordered_non_unique<member<Element,Key,&Element::first>,Compare>
+    >,
+    typename Allocator::template rebind<Element>::other
+  >
+
+(with Element=mutable_pair<Key,T>)
+
+ +

+If default values are considered, the rules take the form: +

+ +
+std::map<Key,T> ->
+  multi_index_container<
+    Element,
+    indexed_by<ordered_unique<member<Element,Key,&Element::first> > >
+  >
+
+std::multimap<Key,T> ->
+  multi_index_container<
+    Element,
+    indexed_by<ordered_non_unique<member<Element,Key,&Element::first> > >
+  >
+
+(with Element=mutable_pair<Key,T>)
+
+ +

+Unlike as with standard sets, the interface of these multi_index_container-simulated +maps does not exactly conform to that of std::maps and +std::multimaps. The most obvious difference is the lack of +operator [], either in read or write mode; this, however, can be +simulated with appropriate use of find and insert. +

+ +

+These simulations of standard associative containers with multi_index_container +are comparable to the original constructs in terms of space and time efficiency. +See the performance section for further details. +

+ +

Simulation of std::list

+ +

+Unlike the case of associative containers, simulating std::list +in Boost.MultiIndex does not add any significant functionality, so the following +is presented merely for completeness sake. +

+ +

+Much as with standard maps, the main difficulty to overcome when simulating +std::list derives from the constant nature of elements of an +multi_index_container. Again, some sort of adaption class is needed, like +for instance the following: +

+ +
+template <typename T>
+struct mutable_value
+{
+  mutable_value(const T& t):t(t){}
+  operator T&()const{return t;}
+
+private:
+  mutable T t;
+};
+
+ +

+which allows us to use the substitution rule: +

+ +
+std::list<T,Allocator> ->
+  multi_index_container<
+    Element,
+    indexed_by<sequenced<> >,
+    typename Allocator::template rebind<Element>::other
+  >
+
+(with Element=mutable_value<T>)
+
+ +

+or, if the default value Allocator=std::allocator<T> is used: +

+ +
+std::list<T> ->
+  multi_index_container<mutable_value<T>,indexed_by<sequenced<> > >
+
+ +

Metaprogramming and multi_index_container

+ +

+Boost.MultiIndex provides a number of facilities intended to allow the analysis and +synthesis of multi_index_container instantiations by +MPL metaprograms. +

+ +

MPL analysis

+ +

+Given a multi_index_container instantiation, the following nested types are +provided for compile-time inspection of the various types occurring in the +definition of the multi_index_container: +

+Each of these types is a +MPL Forward Sequence with as many elements as indices +comprise the multi_index_container: for instance, the n-nth +element of iterator_type_list is the same as +nth_index_iterator<n>::type. +

+ +

+A subtle but important distinction exists between +index_specifier_type_list and index_type_list: +the former typelist holds the index specifiers +with which the multi_index_container instantiation was defined, +while the latter gives access to the actual implementation classes +corresponding to each specifier. An example will help to clarify +this distinction. Given the instantiation: +

+ +
+typedef multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    sequenced<>
+  >
+> indexed_t;
+
+ +

+indexed_t::index_specifier_type_list is a type list with +elements +

+ +
+ordered_unique<identity<int> >
+sequenced<>
+
+ +

+while indexed_t::index_type_list holds the types +

+ +
+multi_index_container::nth_type<0>::type
+multi_index_container::nth_type<1>::type
+
+ +

+so the typelists are radically different. +

+ +

MPL synthesis

+ +

+Although typically indices are specified by means of the +indexed_by construct, actually any MPL sequence of +index specifiers can be provided instead: +

+ +
+typedef mpl::vector<ordered_unique<identity<int> >,sequenced<> > index_list_t;
+
+typedef multi_index_container<
+  int,
+  index_list_t
+> indexed_t;
+
+ +

+This possibility enables the synthesis of instantiations of +multi_index_container through MPL metaprograms, as the following +example shows: +

+ +
+// original multi_index_container instantiation
+typedef multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >
+  >
+>                                indexed_t1;
+
+// we take its index list and add an index
+typedef boost::mpl::push_front<
+  indexed_t1::index_specifier_type_list,
+  sequenced<>
+>::type                          index_list_t;
+
+// augmented multi_index_container
+typedef multi_index_container<
+  int,
+  index_list_t
+>                                indexed_t2;
+
+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/compiler_specifics.html b/doc/compiler_specifics.html new file mode 100644 index 0000000..bbfc475 --- /dev/null +++ b/doc/compiler_specifics.html @@ -0,0 +1,314 @@ + + + + + +Boost.MultiIndex Documentation - Compiler specifics + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Compiler specifics

+ + + +
+ +
+ +

+Boost.MultiIndex has been tried in different compilers, with +various degrees of success. We list the limitations encountered, +along with suitable workarounds when available. Up to date information +on compatibility of Boost.MultiIndex with several compilers can +be found at the +Boost Compiler Status Summary. +

+ +

Contents

+ + + +

Borland C++ Builder 6.4

+ +

+Currently, Boost.MultiIndex cannot be used with BCB 6.4. The +number of problems encountered during the tests makes it unlikely that +future versions of the library can be made to work under +this compiler. +

+ +

GNU GCC 3.3.1 (cygming special)

+ +

+No problems have been detected with this compiler. The tests were +performed under Cygwin 1.5.7. Most likely Boost.MultiIndex will work seamlessly +for GNU GCC 3.3 or later under any platform. +

+ +

Intel C++ Compiler for Windows 32-bit 7.0/7.1

+ +

+member not supported, +replace with +member_offset or +use the cross-platform macro + +BOOST_MULTI_INDEX_MEMBER. +

+ +
+ +

+Altough Koenig lookup seems to be officially supported by this compiler, +some issues have arisen seemingly due to bugs related to this facility. +In these cases you might need to explicitly qualify global names with +::boost::multi_index. +

+ +

Intel C++ Compiler for Windows 32-bit 8.0

+ +

+No problems have been detected with this compiler. +

+ +

Metrowerks CodeWarrior 9.2 for Mac OS

+ +

+No problems have been detected with this compiler. +

+ + +

Microsoft Visual C++ 6.0 Service Pack 5

+ +

+member not supported, +replace with +member_offset or +use the cross-platform macro + +BOOST_MULTI_INDEX_MEMBER. +

+ +

+const_mem_fun not +supported, replace with + +const_mem_fun_explicit +or use the cross-platform macro + +BOOST_MULTI_INDEX_CONST_MEM_FUN. +

+ +

+mem_fun is not +supported, replace with + +mem_fun_explicit or +use the cross-platform macro + +BOOST_MULTI_INDEX_MEM_FUN. +

+ +
+ +

+No support for index retrieval +and projection +nested types and member functions: +

+You can use instead their global equivalents. Also, this compiler does not +implement Koenig lookup, so you might need to explicitly qualify these +global names with ::boost::multi_index. +

+ +
+ +

+The lack of partial template specialization support in MSVC++ 6.0 +results in some inconveniences when using composite_key that +can be remedied as explained in +"composite_key +in compilers without partial template specialization" on the advanced +topics section. +

+ +
+

+MSVC++ 6.0 presents serious limitations for the maximum length of +symbol names generated by the compiler, which might result in the +linker error +LNK1179: +invalid or corrupt file: duplicate comdat +comdat. To overcome this problem, you can restrict the maximum +number of elements accepted by +indexed_by, +tag and +composite_key +by globally setting the values of the macros +

+This operation results in a modest reduction of the lengths of symbol +names. +

+ +
+ +

+Under some circumstances, the compiler emits the error + +C2587: '_U' : illegal use of local variable as +default parameter, inside the MSVC internal header +<xlocnum>. +This problem is a recurrent bug of the compiler, and has been reported in +other unrelated libraries, like the +Boost Graph Library, +Boost.MultiArray, +Boost.Regex, +CGAL and +MySQL++. +The error is triggered, though not in a systematic manner, by the use +of multi_index_container iterator constructor. Two workarounds exist: +the first consists of avoiding this constructor and replacing +code like: +

+ +
+multi_index_container<...> s(c.begin(),c.end());
+
+ +

+with equivalent operations: +

+ +
+multi_index_container<...> s;
+s.insert(c.begin(),c.end());
+
+ +

+The second workaround has not been confirmed by the author, but it is given +on the Internet in connection with this error appearing in other libraries. +Replace line 84 of <xlocnum> + +

+ #define _VIRTUAL	virtual
+
+ +

+with the following: +

+ +
+ #define _VIRTUAL
+
+

+ +

+Warning: it is not known whether this +replacement can result in unexpected side effects in code implicitly +using <xlocnum>. +

+ +
+ +

+In general, the extensive use of templates by Boost.MultiIndex puts this compiler +under severe stress, so that several internal limitations may be reached. +The following measures can help alleviate these problems: +

+

+ +

+Microsoft Visual C++ 6.0 Service Pack 5 ++ STLport 4.6.2 +

+ +

+Boost.MultiIndex works for this configuration. The same limitations apply as +in MSVC++ 6.0 with its original Dinkumware standard library. +

+ +

Microsoft Visual C++ 7.1

+ +

+Problems have been reported when compiling the library with the /Gm +option (Enable Minimal Rebuild.) Seemingly, this is due to an +internal defect of the compiler (see for instance + +this mention of a similar issue in the Boost Users mailing list.) +If /Gm is turned off, Boost.MultiIndex compiles and runs +without further problems. +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/examples.html b/doc/examples.html new file mode 100644 index 0000000..87e581b --- /dev/null +++ b/doc/examples.html @@ -0,0 +1,319 @@ + + + + + +Boost.MultiIndex Documentation - Examples + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Examples

+ + + +
+ +
+ +

Contents

+ + + +

Example 1: basic usage

+ +

+See source code. +

+ +

+Basic program showing the multi-indexing capabilities of Boost.MultiIndex +with an admittedly boring set of employee records. +

+ +

Example 2: using member functions as keys

+ +

+See source code. +

+ +

+Usually keys assigned to an index are based on a member variable of the +element, but key extractors can be defined which take their value from +a member function. This has some similarity with the concept of +calculated keys supported by some relational database engines. +The example shows how to use the predefined const_mem_fun +key extractor to deal with this situation. +

+ +

+Keys based on member functions usually will not be actual references, +but rather the temporary values resulting from the invocation of the +member function used. This implies that modify_key cannot be +applied to this type of extractors, which is a perfectly logical +constraint anyway. +

+ +

Example 3: constructing multi_index_containers +with ctor_args_list

+ +

+See source code. +

+ +

+We show a practical example of usage of multi_index_container::ctor_arg_list, +whose definition and purpose are explained in the +Advanced topics section. The +program groups a sorted collection of numbers based on identification through +modulo arithmetics, by which x and y are equivalent +if (x%n)==(y%n), for some fixed n. +

+ +

Example 4: bidirectional map

+ +

+See source code. +

+ +

+This example shows how to construct a bidirectional map with +multi_index_container. By a bidirectional map we mean +a container of elements of std::pair<const FromType,const ToType> +such that no two elements exists with the same first +or second value (std::map only +guarantees uniqueness of the first member). Fast lookup is provided +for both keys. The program features a tiny Spanish-English +dictionary with online query of words in both languages. +

+ +

Example 5: sequenced indices

+ +

+See source code. +

+ +

+The combination of a sequenced index with an index of type ordered_non_unique +yields a list-like structure with fast lookup capabilities. The +example performs some operations on a given text, like word counting and +selective deletion of some words. +

+ +

Example 6: complex searches and foreign keys

+ +

+See source code. +

+ +

+This program illustrates some advanced techniques that can be applied +for complex data structures using multi_index_container. +Consider a car_model class for storing information +about automobiles. On a fist approach, car_model can +be defined as: +

+ +
+struct car_model
+{
+  std::string model;
+  std:string  manufacturer;
+  int         price;
+};
+
+ +

+This definition has a design flaw that any reader acquainted with +relational databases can easily spot: The manufacturer +member is duplicated among all cars having the same manufacturer. +This is a waste of space and poses difficulties when, for instance, +the name of a manufacturer has to be changed. Following the usual +principles in relational database design, the appropriate design +involves having the manufactures stored in a separate +multi_index_container and store pointers to these in +car_model: +

+ +
+struct car_manufacturer
+{
+  std::string name;
+};
+
+struct car_model
+{
+  std::string       model;
+  car_manufacturer* manufacturer;
+  int               price;
+};
+
+ +

+Although predefined Boost.MultiIndex key extractors can handle many +situations involving pointers (see +advanced features +of Boost.MultiIndex key extractors in the Advanced topics section), this case +is complex enough that a suitable key extractor has to be defined. The following +utility cascades two key extractors: +

+ +
+template<class KeyExtractor1,class KeyExtractor2>
+struct key_from_key
+{
+public:
+  typedef typename KeyExtractor1::result_type result_type;
+
+  key_from_key(
+    const KeyExtractor1& key1_=KeyExtractor1(),
+    const KeyExtractor2& key2_=KeyExtractor2()):
+    key1(key1_),key2(key2_)
+  {}
+
+  template<typename Arg>
+  result_type operator()(Arg& arg)const
+  {
+    return key1(key2(arg));
+  }
+
+private:
+  KeyExtractor1 key1;
+  KeyExtractor2 key2;
+};
+
+ +

+so that access from a car_model to the name field +of its associated car_manufacturer can be accomplished with +

+ +
+key_from_key<
+  member<car_manufacturer,const std::string,&car_manufacturer::name>,
+  member<car_model,const car_manufacturer *,car_model::manufacturer>
+>
+
+ +

+The program asks the user for a car manufacturer and a range of prices +and returns the car models satisfying these requirements. This is a complex +search that cannot be performed on a single operation. Broadly sketched, +one procedure for executing the selection is: +

    +
  1. Select the elements with the given manufacturer by means + of equal_range, +
  2. feed these elements into a multi_index_container sorted + by price, +
  3. select by price using lower_bound and + upper_bound; +
+or alternatively: +
    +
  1. Select the elements within the price range with + lower_bound and upper_bound, +
  2. feed these elements into a multi_index_container sorted + by manufacturer, +
  3. locate the elements with given manufacturer using + equal_range. +
+An interesting technique developed in the example lies in +the construction of the intermediate multi_index_container. +In order to avoid object copying, appropriate view types +are defined with multi_index_containers having as elements +pointers to car_models instead of actual objects. +These views have to be supplemented with appropriate +dereferencing key extractors. +

+ +

Example 7: composite keys

+ +

+See source code. +

+ +

+Boost.MultiIndex +composite_key construct provides a flexible tool for +creating indices with non-trivial sorting criteria. +The program features a rudimentary simulation of a file system +along with an interactive Unix-like shell. A file entry is represented by +the following structure: +

+ +
+struct file_entry
+{
+  std::string       name;
+  unsigned          size;
+  bool              is_dir; // true if the entry is a directory
+  const file_entry* dir;    // directory this entry belongs in
+};
+
+ +

+Entries are kept in a multi_index_container maintaining two indices +with composite keys: +

+The reason that the order is made firstly by the directory in which +the files are located obeys to the local nature of the shell commands, +like for instance ls. The shell simulation only has three +commands: + +The program exits when the user presses the Enter key at the command prompt. +

+ +

+The reader is challenged to add more functionality to the program (for +instance, implementation of the cp command and handling of +absolute paths.) +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/future_work.html b/doc/future_work.html new file mode 100644 index 0000000..dc709e3 --- /dev/null +++ b/doc/future_work.html @@ -0,0 +1,273 @@ + + + + + +Boost.MultiIndex Documentation - Future work + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Future work

+ + + +
+ +
+ +

+A number of new functionalities are considered for inclusion into +future releases of Boost.MultiIndex. Some of them depend on the +potential for extensibility of the library, which has been a guiding +principle driving the current internal design of multi_index_container. +

+ +

Contents

+ + + +

Hashed indices

+ +

+Several STL implementations feature hashed sets as a natural +counterpart to std::set and std::multiset. +multi_index_container can also benefit from the inclusion of hashed +indices. As the exact details of the interfaces of hashed sets differ among +library vendors, a good starting point seems Matt Austern's paper +A +Proposal to Add Hash Tables to the Standard Library (revision 4), which +has been submitted for acceptance into the next revision of the +C++ standard. +

+ +

Ranked indices

+ +

+Ordered indices are implemented using red-black trees; these trees +can be augmented with additional information to obtain a type +of data structure called +order-statistics +trees, allowing for logarithmic search of the n-th element. It +has been proposed that order-statistics trees be used to devise a new type of +ranked indices that support operator[] while retaining +the functionality of ordered indices. +

+ +

Notifying indices

+ +

+Notifying indices can be implemented as decorators over +preexistent index types, with the added functionality that internal +events of the index (insertion, erasing, modifying of elements) are +signalled to an external entity --for instance, by means of the +Boost.Signals +library. This functionality can have applications for: +

    +
  1. Logging,
  2. +
  3. interfacing to GUI-based applications,
  4. +
  5. synchronization between separate data structures.
  6. +
+

+ +

+The following is a sketch of a possible realization of notifying +indices: +

+ +
+struct insert_log
+{
+  void operator()(int x)
+  {
+    std::clog<<"insert: "<<x<<std::endl;
+  }
+};
+
+int main()
+{
+  typedef multi_index_container<
+    int,
+    indexed_by<
+      notifying<ordered_unique<identity<int> > >, // notifying index
+      ordered_non_unique<identity<int> >
+    >
+  > indexed_t;
+
+  indexed_t t;
+
+  // on_insert is the signal associated to insertions
+  t.on_insert.connect(insert_log());
+
+  t.insert(0);
+  t.insert(1);
+
+  return 0;
+}
+
+// output:
+//   insert: 0
+//   insert: 1
+
+ +

Constraints

+ +

+The notifying indices functionality described above exploits a powerful +design pattern based on index adaptors, decorators over preexistent +indices which add some functionality or somehow change the semantics of +the underlying index. This pattern can be used for the implementation +of constraints, adaptors that restrict the elements accepted by an +index according to some validation predicate. The following is a possible +realization of how constraints syntax may look like: +

+ +
+struct is_even
+{
+  bool operator()(int x)const{return x%2==0;}
+};
+
+typedef multi_index_container<
+  int,
+  indexed_by<
+    constrained<ordered_unique<identity<int> >,is_even>
+  >
+> indexed_t;
+
+ +

User-defined indices

+ +

+The mechanisms by which Boost.MultiIndex orchestrates the +operations of the indices held by a multi_index_container are +simple enough to make them worth documenting so that the (bold) +user can write implementations for her own indices. +

+ +

Bidirectional map

+ +

+Example 4 in the examples section +features a bidirectional map, implemented as an +multi_index_container with two unique ordered indices. This particular +structure is deemed important enough as to provide it as a separate +class template, relying internally in multi_index_container. As +feedback is collected from the users of Boost.MultiIndex, other singular +instantiations of multi_index_container might be encapsulated +to form a component library of ready to use containers. +

+ +

Indexed maps

+ +

+multi_index_container is rich enough to provide the basis +for implementation of indexed maps, i.e. maps which +can be looked upon several different keys. The motivation for having +such a container is mainly aesthetic convenience, since it +would not provide any additional feature to similar constructs +based directly on multi_index_container. +

+ +

+The main challenge in writing an indexed map lies in the design of a +reasonable interface that resembles that of std::map as +much as possible. There seem to be fundamental difficulties in extending +the syntax of a std::map to multiple keys. For one example, +consider the situation: +

+ +
+indexed_map<int,string,double> m;
+// keys are int and string, double is the mapped to value
+
+...
+
+cout<<m[0]<<endl;      // OK
+cout<<m["zero"]<<endl; // OK
+m[1]=1.0;              // !!
+
+ +

+In the last sentence of the example, the user has no way of +providing the string key mapping to the same value +as m[1]. This and similar problems have to be devoted +a careful study when designing the interface of a potential +indexed map. +

+ +

Serialization support

+ +

+Once Robert Ramey's +serialization library gets accepted into Boost, support for +archiving/retrieving multi_index_containers should be added. +

+ +

Move semantics

+ +

+Andrei Alexandrescu introduced a technique for simulating move +constructors called Mojo (see his article in C/C++ User Journal + +"Generic<Programming>: Move Constructors".) Move semantics +alleviates the computational load involved in the creation and copying +of temporary objects, specially for heavy classes as +multi_index_containers are. David Abrahams and Gary Powell provide +an alternative implementation of move semantics in their paper + +"Clarification of Initialization of Class Objects by rvalues" for +the C++ Evolution Working Group. +

+ +

+Adding move semantics to multi_index_container is particularly +beneficial when the container is used as an internal building block in other +libraries (vg. relational database frameworks), enabling the efficient +development of functions returning multi_index_containers. Without support +for move semantics, this scheme is impractical and less elegant syntaxes +should be resorted to. +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 0000000..5147aa6 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,74 @@ + + + + + +Boost.MultiIndex Documentation - Index + + + + +

c++boost.gif (8819 bytes)Boost Multi-index Containers Library

+ + + +
+ +
+ +

+The Boost Multi-index Containers Library provides a class template named +multi_index_container which enables the construction of containers +maintaining one or more indices with different sorting and access semantics. +Indices provide interfaces similar to those of STL containers, making using them +familiar. The concept of multi-indexing over the same collection of elements is +borrowed from relational database terminology and allows for the specification of +complex data structures in the spirit of multiply indexed relational tables where +simple sets and maps are not enough. +

+ +

+Boost.MultiIndex features additional functionalities, like subobject searching, +range querying and in-place updating of elements, which make it a convenient replacement +for std::set and set::multiset even when no multi-indexing +capabilities are needed. +

+ +

Contents

+ + + +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/lopez.jpg b/doc/lopez.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ac37b4a94eb4303cfab9af0394509252dcd46bca GIT binary patch literal 15167 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 XY@IpB%IoJURbIAzK6}xQ|Nl(@bbHGq literal 0 HcmV?d00001 diff --git a/doc/multi_index_cont_example.png b/doc/multi_index_cont_example.png new file mode 100644 index 0000000000000000000000000000000000000000..120cfddac53af234463e20c34cd51a49d8a0b42b GIT binary patch literal 87417 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°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 ~J}NS$q|IQahl8oRg}_w9z83~N8~{I*QDQ|?=) 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Ⓢ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 N@X>*3Y|Kmy)&O#?mni@M literal 0 HcmV?d00001 diff --git a/doc/perf_1o.png b/doc/perf_1o.png new file mode 100644 index 0000000000000000000000000000000000000000..061c8ecfb93f3b11d7b6b884a64a9d1ec8e034ab GIT binary patch literal 13186 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 Tm-7Gv0|SGntDnm{r-UW|DHG`` literal 0 HcmV?d00001 diff --git a/doc/perf_1o1s.png b/doc/perf_1o1s.png new file mode 100644 index 0000000000000000000000000000000000000000..4408092ad5633d73352ccae56804e3ab9bdea4f6 GIT binary patch literal 14190 zcmeAS@N?(olHy`uVBq!ia0y~yVA5e=U@YNaV_;w`cMYmwU|`@Z@Q5sCVBk9f!i-b3 z`J@>b7+BIBeH|GXHuly04xG%uz@U)q5#-CjP^HSi(9q1l@bfEaktaVse$A>qe)11lS;&fkW5LBWEzAro7cve! zU|?W0kutCVnI;gtpznwgheE@Q4_DO;Y-AXela)PRF*0Rr2|L8dz?8A2=O7~kQ%9;w zh0xx=o<3}h918>m4W3L9VPHIBvL)C_nYBvN@W+WH2F4>zdYZAEsWeFrt#jQ>v*Quwxw*@<_Th2XQvbLXDq z&uw5xQoI(hNN3>z#hwKX#*#Lg>JV*a9rtQKb$%He7tLfeREN zC4X3jLBS?=h>3xzV_759>?McIBHPa#KXz<6du-pWxls)aNisRXOwEaNBo!LiV!ui9 z6tW07EKcPT1KHo$#0t{BAc2vA@kq=11B+cx`srV|aiinee78AuE}&%Pvf_ZWUaxh| z>z2Zwyb29&oB2UH57(Juh76Dwx!ve)g)-kUbc8~W`%|Vz3S!R zjNG3jwmAvhJv=3Eg+qKd`)BP>hMWcr0uDM`K1eK-P&wAza%F;)$BS;^iOh_)ilEe> zyNO$qPp&6A3lvULvKtTX3h#(v5ODChq7mcxQ*?=Fo90R*c4h_+g+ju;(^KovWm z1B0NCX0xLA!64ouRd>JH9T5xy4$f(u*0$zhj4T%>$oMb(dd;SRL2A*ufcVGiJ_ZVCv|+Vc0g2g@Gxf#6+GO6!i`VnHZQdOaeB5(iX3X4ue8N zN8A?UN!t!Euv~cYO&lb^X7iipm|?>5ybYptMtk1*BfJ<{eAW@vRuf>&p4v8=)XvMP6LBdg}uc(-H;cy z3oZ(=MoX9p33g7|ZvG+nqr?5T4STGaZ7(Vux7=pI%Ap{ZwdB&mSpglk(_8LvEDb+A zdCFV;A7yJFynfmQD)o4tbu2DT;NRY?m%+=xlyO02+Qkm(KMV>DY=I}6ZY`2$ZRTfS z5jcAIZ%Fooc?)eOPP}~9B~Rwk@2+zT*%+8QSmXUU;++dfW^ZZJ=1b_ zfJ}anW%$_3;KkaP3-6^K^H_3XY46F5tsOj|XwTz_XZqD|v5KiChxPFZKN|){moJHh z%61PEGQ)ef9AOr4SgguDlau}SO2@;t9~oE#?rpa0oY|?8mio`b!(xL=)0}{|=m3zr zggX~^&)=XflcRbjz~6>}@rX-~k_77%RhcJ8oZoG{$jl4MKvRO6S8VLMD4EgI!n}T2 zPkY&HHid>2_YTPVYN$*zbI!OTq0pd__n?o7Yu6)Z9tIYH&|{HqtLIIen83i)p>Xh1W#Od5$N9KooSu<>7*uFC` zWdyt_Rr_WmA#q@v3Da%Q?iW>@rq3?kZOr4>E8e*B_|~=Jk$kiN9R$TyK#22;8|XJHwV+~%)b8cYd-Sy{y{zlrjEjrHT5C_+@N~0(VDA+je*f* zRs=2w`RYJ@)|rYLpcCgreBe#&Z+Zxdw5SVBTICdS3pnVs z2p!3Ao@UkAwCvlQqr&dTc0BI6@`IT}VUF^SC)1>yS8h=>Z5I~)xn!2}*-5N_Ku7iRTVDWEXlxfVS%U4ktb$eU8Qe~H})>>Y+_*$aA-E#*yDEZ0Sg18 ziN@}8i#byZm>L)kMQsw;J+Wqmd4W4)CWAslgxl^TiyJ`YbA|quCHe1DGVYbyOafKy z^E!_hui#{_?sWgl;-$U0T^UprSl;pPnkX{WCEZP!K@+51qdVlya;=G>t#cR7tNY|V zb73GmU&w4Bg$56~^n+om0<3y$5@H>{Sc*=*sKqJIz@Z=(B)xq{-!7>xhGf2{_9~1Q zt12G_u!5RLw@QyqYB+abnsVQ_a}z*C>5Itpq*WRLj~G>FTnuV!WK?K~V2f7H+7KGJ zz=fH+YvQ~NP|dz$y1*3=lMg{@E$UL>6z;KR)&%LJ%DNZ9O+HhXI8A2lOt&Cq355o> zvv)28M8WpdHYQN*#`cck>x>Jl zS1)7$<&LvP4!JWIa)67$3~=i2SoE;YDg0(%(?eDUrj93B9Jf>NEKW#aXkbu!%{BAl zwzDTd@od#Zezxj`l~bF9PQP${pbTm*J+SRa<#SJwK0uCYj zuUCrj#DhG@d#CZtO6N7Z*f6Nzbj10zPXaSBtlCW$WG@ zN1E<&D{c!nveG(x0MznPR&-3b-P&+QUG-qklYgK<{st?)aGL`41tMd^N*D)bp2d5g z2O4m>F@}Ot*EOfb2_ijAt3Xw~ZiL%G4GGo|b_Pb1s0}?$fd*VF_&^bnAj!HoK_ozy zf#pI+OrYn>R~}hw)~Q_Q$$Bk)s`}Rp*|x;Pv17xDgdaj8 zW|c9^B_?LGFD-k#tFn30Qw3n}Lmw{FWntf^<2Q+*y(WkF-1g-bdp>aT&JQ!CTbPd7mRJjd0GHFh9A zQ(#?C7@Xo0VXNHv>~h{VQH2IKuy5K@7rIn%vg@WNGcskAd?*v?X>#Q9IQxCc94U|2 zs&b&Lodl0*fxA&UJI?93+OIDa>0!FHWWSq?Y5Cub2NxI^SS~b_wQ|TZ^~Nal>b;g{ zozlp*M^x@?ZhnTsUIUx%Y4JTD!|(j~B)qOmdb(87mB-AW=G{Rjuh&esJwM&x60K=H zpOKyRWSfS^`ZcdSb2eU3j}^S7-4fR!u$nFGM#Aok4I%plt1q({{uEPah&a)`=YPuT zqsQfSZP?1cH%yy3|61&%gZG{uye_rSZQqgYDf8#p_<6{I`ctiK9`SRcX3mP(&8GCZ zzv&?}B=J7z@y$tF>bgu$_JR!S7AA1&=9#?g%ZmAK2NNLNp0sVS)Lznf2ayV*but*P zZ)&^9xjN8*s{@oVZXtr`LPj^M>>S1QuFlyG-D1M)TqCbU1RHX>nK5uE>=M&?vHEM< zrLL$7H5=mt-};*Z4Okc)7%tWdD`n~0lyJ>_7}5P$Yt4a2YFn~3dz&8egW4J~{~de& zWH36#?>CxvP$$KWdsd)>4ya%>oEGS?;Xjm5nXNe?$8Zx>OZj3iuc|2;i zERbXcrwlQj#SMR)*sl0mHLD%7h&s{3#KzXZAob;S#D-WIdoO;zSqEDLZ)GL2h#M*J zX!=MTGkKL?iCfipj7GeB&EbluUlP3o$>#I(=+=W z4UP}~Ul8MMk;{B&0;(W;%UazSb9Z}XJUUUCQ&N-mgQ!iai%x7|ClaF9w69+v>8sT&&A=jX zmjCjx@Pq~P@7Qy>aZARup2=`D-U14~J=!j3H2*Btof_znrDz;=fhUhaz`342&)%5X}oiPV{U`ViRLv5%7#%c=1rGNROD& z;syy&7WV$-ez4=U-oqect`+PIOdU%M#d6+&%8i2>4(y<&`bN$Px*z^~-s9@4JHll4 z50tPHK;g0I=cKwXslS)_1@?>CEN-}UcTu22HOMFSFFFGnoig@cco8)-(15E=j)6mA zlh}!gtL5h3yU3Ix(gR7=U*0_fS;Gh_o_fV}NGro3r3C)+??u#x1?$(z#;&vHay$5e zg@KXf)Xo1dx4sg+U%B(iDc&j7?UUy!pIoo3S;H>Gz#aR>z#&#PdXY zj=k1607}+d5)Mv2(e^r*Wyc?{SLeQ7tdL~|b$fc_xcFI>H7E8ik=AtQ?r&rSXUqpH zpSn3MlKJ{tV*x8DGj0XdYhpUiYYH6Z@wqL( z$jy}^RVmXeQhQ_B!jCHttJ-t9iGfURO6_2*;@=`%-GAi;%K;4sP($2G+XPWHh+(e| zNDXg+id~f^5}>l4^$H89OS`KLQs-Q~#-*&x3>qo|*97=0USc8*Qp~$yrizKI+)Le6 zgC4JNjXCag>2O}v4A1u#GABMrv(5n3bukBxB80`(hC4j|^u6F@#$xRg?BWa@3j|Ar zmEHto)_nh|q#pES_UDd^&E5TZa+-O{$McemrBoj_p0zjFJmKlbR&Kq|g{|rfnI=VS zzWBG}?B2z!qNyN*L%LgMFfS}r%}A*XI_qHGA7RYV`S6qUug*U^{7$Q@eNH$%W67^} z3Hw7v#&(}4T%C|@W@f)2Tw!r5*He)8_j$__6J1}kcx4@Q=&DYb$rCBB`cE-jU-xjq z7KVkg;7DZkzdU2cM~;}P4W+Fbl9v@y%&T~N&e~47;oep6!Kt3K+wMkG(-N^sEq{z} zi7o~XXPL0>otO(Ke7hL|=NE(=UFBQEgEI@w7H zUKea%ynH#Rj;Ub)4U{AtESn_x>0;Xp^>RV0$Q*FHcWk^H;ozaKSmC0p%5qg_(vB0Wr(K5=~#`rCLant{<|S055Sz z0uEG!J>gcoqbr!i#S^t91=PLSRSAkB{Eg6(9@gs%1lA>g1~pxMtyKPh*&65oO3%v< z3VvL?I@|rA#)7baj{A>y2mCv^l073+#~Ly2 zoo5Zb&R>*a{c`fm6^ScLQXLK7aIq%8DrxOl`)1?Ih2I!&gXSUf6b-rDxEC!9ir_hV zsB?;;=oc|o&7V`E*mD*$H!v((anva40?&D-rYlS-CAZ2%7BDw3C{_2QZmBl-62-Nc z^{B=IP+@gZ?6iRf#|d2lw?hk_X-I&oXPqVL_a;WpxS%E6w~f#3U;(Iacq>WrAnU=5 zg=SnCte}t&%Pn2js5+@Xo;ph5xG3>0y0v1ryzJFs++0`q-9vEIM2=SXkk;)XAWSoeNyTHPSYdRUl&Mc^j)6SspJ3R|;V);37y z?&a|ZH4`tNUEJ_4cJW1~v>%{;cC3Oq7uyz)QDEtm`z;orUd5H1y**+E;O22;^Y#<% zX1z^;4ri~O&R{gQVQ^rWxV5kKiWs*Kf0cMviR$|dupj0zC^WpVy4!Q_mg4pWQmluU z7bso;_O!yb?gK^qoqw<_?B?%(F;#;6Hbs+8S$C&N2g z`Yur*AbRSik7Rcu3xm&7I0)18VX{>nu*zS)5={((iTMdVy)5UA2H4cErf|htcO#G4&o*}}s>`-gKYgV%f-F@ziZy6jI1a0ry1n5q= z$dn^8;Z~7iT2z(D1#n|tcUq%`W8Rk2rd%s-R!Opc0gVy6WU}p!^Ay(savGMElmX$2BwZ@%fBp^=QFMPdt5_;^{_ms zf3(?Yi5SC;zIiVDf8>n}jrV5hdr z)mbTDe)WTT4XO+r3mD@ot#2uTJ}615pTIYQfWL2mvzhsY z_4Es-T_6X@flA5v4Ly3Zq*)gyfE+0G7Vnq^nKmLfpA=Y6zt{@*Ve_J+Mo~LJ#qu5o z2L{7`@(re+Z>;-$K-G-P?O*|ea%MJ{#(gFhfzt^EEWe(nzL;nzaD5lwmC#MfN7pOs z*0@W7I`P{g5*T~Vp1Nqg(EXr>gq^Nf1H+>1Zr1pNhM;a`paH10)$3;tYIwni%HGQN zd;AajY<51dffGFbpt$I_*(9Ue9^JPC9i%~F&&nOOOp0@6_LcR@s}D(VK!gN2iSPrG==OzE$6)Cy2~y@1*ZWcBtn3rgd0`)dWM$Q-^wg14=3A(PG? z&{X_Uv^FiYl3R>pkPBafiNPV`@39xR`T`wt!Hzx6!T2R+ok>(cFQ~lGjcAKF%`D&R zEvs1EVLCrzmV|i^cv>Q3OM>LXR*{`+d7WOl^{YyN3S2ktX&2WCM#ick34Mn+eZ@T~ z(2y{ddR=JY1!{}oALYXr0I-bAQIc@%1>18_j}$ag3d&9QjzK$d-&(+3JWwWY3N&~D z>V%p>22=-|(8eCNgCE!z2s0%nEbudJ+nKn)KUD1rU)Jv84L_jXZQ0-?eMoZWVO%~HQo7cLD=K|sDEzj3^;E3pN})xwcA5- z;eymZ0m`<5MYl7gWOCQ^G&Qnfcv1s<4&CBG_AhU6WQLe9$$9y)P? zYJ=F;8UcXL_9i$^`gMjeNC2Tq?)4)v# zP`Q%V+j@-AnEQGMkD=_liEN;*i&qBJLkBz9miRorP~^oC&fGfVB4?6WFVELrRYbx9 z)q-3Z7j`HvZ*0mvmFF?hqVQvr=trd9sh!TWw%~i7LaVq zV=86Hz-Ur+3Di=9xt`4y)XQZ9^(x2_a@`)nuvlCT=EDVBU$ zNkamMP}?6VF{kS>k0&`#Q!lOm1ug+Zc=m1DFj3{}|Nqzf*=}h_1c6dvTNzvGk()OT zsJrVxrlP>UU~L91(3_CAA;CIv+Kppuo8r8rv=^)2fd=de#-4P}E4s&?lpiR(nt0?| z=ntzmM@sH-UTtn+W9kU>=VahmkZ66#DQ7WjyvyVwsRtQ{!G(>(-A`)^O$6K|4wP@> zGA!lsk>BUWz0j)f3LD#ADQ(bHfzFn*1wAb8rfk`VK_lm&N>N%=Sw-$V-<-%7%0?%8 zd4t}uI*F@4Pp~X{BuGvo%M7LLy_fVW6~nb72YCsRp+e zNbvP;z8GuYyuQ#m^KR$*iw$cZ_pe^;m$8KHrcREz!{g23_cOS6X8RN}HZUx@%>hZX zTLKNEEc=@R9b#fV&jtQ{a_#UHqwY&z4O&1G4W+!NGn~_Ygoi8^J^3L=p~p-M6sxS@ zWioofPK$dd2O8Yckci>!xY+gr9A_&)W}oKZ-dLQwIAKem!5q*~79{pTlLHwgEvX_r zE7?wEG$)wIfZCI+;MwIDAlIzr01JQ`6rd8_L_QIzz&fb0K!kxs;HtFF5jg?#7%Afy zbt)xX4+0(=8<_BD|L^(a{&Vr4|A%+%kzze8303&!u$#rL(wq61wQ+3;}Qf0`~vngbmCM3`vr_O@R&k z6L#InC`rhL_|c6~7nDuzse=185|DD(5j6h)Wy$S8ho^2@+e!?i^k7AW1~arkIlzBz z!isw>JKWABFe;l$8qHHRoYUjJ`4Biv6F`%#KPTs1ly-j6&h@aEf8FL_H}_Vybmj&z z-uV}gT>+~S;o$`>t*heY-q*%zW|?A`bYpSDH{0?_2WB>Y7B^Jpxsvzi!WP5QG?TU9 zT+t>5>K(rqT6ElV`-x|Qw{JM?vuc=h>{Nl()^d@68v@d~ED#?vo&_a^)Qh@?y%Jfb zT^^Yw376epWpMwx_(xf`XI-g;8?V8XHp{AA^1O#Z!Nyn%a_dfrHm88lSv}N&Z5T4A}aSX_EW7Z;N?nfZbpUj)rSCpweMM)?-$m z`HoZNa}R)hqycL3R85BrIc@6|XY*iYbFJILXbe)U09x(ZFsH;CGC?7Dr{drxje}Pk zBy;zGH79`9x}~(FmVmR}#bex?Y}T=zdGP|An;1ZA+}>?B?eAL$QhckzF>k{H^SBGr zYKt$sgCd;?G>9{+e?!9UXEt~EvZQlcK;s*6hg+{4JDtItCeqV15i}F`O#Nz&vWD2Bnnb-Pv{j-lG2038XIU)(5}2}x!fh7(Bop;_0j3o3TU{18#6a@vfa$Z zZ(B)%Q#Ytqxp<7*RLWqnD`>273TRwO?Dnk0ULLQrvY^Iopuw#Q#qA%P?ionR)=IK2 zZrBAH?#j3u;TX5wsaw|R$*lG@?0@T&Q_O`oNzaZ-`QLYG@fQu-9hq#xZar*VD?TzE ze=_Uz0gl`m21&A=j>i^%bZD)RVLdFzz#?$hEh2+)gK9*Pl))1}{)f9-rPS=a`Y$d1 zBCx1X=US@8XZb_+&MR$a^ooUitGBR~l3#LpaRO-QD@pQH-|_8kthYr3x3%=%(~#V2 z2l7dv!`ZoRJ5K96@k-pu0u_Xvph2gqXyad3xYtS>9J$f#dluFPUjNal_bW8iJU|1q zpJSbNh~4c2b?ZU-rdRxww1GkC61}Gqx=ro}13*fy3r@RuG!T@Ynl2vWe#$FRvsfQg z)P{rN)z%^pT7XIGU&-CmV;1Fh&;vAie7*Uxo3tTV*ThQ|3!nHY7fBf`?wuBBa0e8b zwJoV7?B_sMCa9RkytrEdN`^Z?9b#En;mw%0r^hS?G#&_AA6UW*&L{~YJOz(iZ(U~s zMYIefXu9U0)0?@DhgTXJ@A?f|P!eb`LkjFZchC}}{0#!d6}JmkKgcXx_!w;6mXAia zK(;Y})+40_JN?jGCN)C8|_B1VIax4Ga)ASHDZ}F@< zZ=$sN6}i}jWrvftYy=N?fU6xIKhS{f7899k3=YS)Z0o;rjPF+mPlxXr4FMn7T~S|x z>)c-kR!xyx>YjP)5+q?R`Z+~l-4U0wZQMnNJ1({HXE&tmTy5syRy}ts!(T6kFV4^1elGq4R^TuHPebA!sNRtVuQwpQq3cFPjS+2|pI_h? zb-KTqfAVoPyIuS1At4M(t)M7q%WYI>d4aZAC|nRY{QqHuqj1)S13!3p zctlLY-1^&~>FXe~oAsQ-TQ)MS$!28gNHs}ZY?kIBo9h^69b|NRMZEi8t5@d(_w$AA z?`wJpTESa#QTOl*6L573F31dSfEoa|H5bB?Kd6|Q3>zXs>SnMmPPhOnvvUzW4H9#49xa%&A7nWx8us=UA@yI{w}%Ie!70j5Apbl z#Lx08>%f_p2ed+cu`(##UyyjPsv@!I#*>ex4HvEo7cZ1GeS7$+eb&$E6aHV2_E_8t zZa=U=gV=z}tw6ae>{Vu_+_s65Q9BC3sT92W1(FyVc-fw8RsNwB6LeMhl7EIR$I4eL zbM5W^zIbSGD*|LUXmP95-aiFkji6SV1Z(r|jZAMAfX0GAT?a@*8#FaZS$`bScOFb; sDSUX^zWAU4#N*(K^JH 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 P0|SGntDnm{r-UW|{TK0e literal 0 HcmV?d00001 diff --git a/doc/perf_2o.png b/doc/perf_2o.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb820b696fcc814e9f91e78350a38b43dd23b91 GIT binary patch literal 13043 zcmeAS@N?(olHy`uVBq!ia0y~yVA5e=U@YNaV_;w`cMYmwU|`@Z@Q5sCVBk9f!i-b3 z`J@>b7+BIBeH|GXHuly04xG%uz@U)q5#-CjP^HSi(9q1l@bfxI~z8#ZW%ftlo_Pu>V+Z z+md=+JyV$k9CW^HNYP-z9?};w4j?%SB@&=gEOI>Zylf#10uDNhAM4KmS+Q9_j6uMG zd6Ou6^nLD1*P{;WUD!Dkj$OQI>H~84O?GYujs+JT4l;op7q9{BI1wENg@zd%SH+x5 zo7QgJD0tSs*X4Q#17l0M{3VCb&Mgc##9|+Rl6GL|O_K*HZTp)iRJ82|1IvXC{31m~ z_6`h!IvMTtUP(t1m{|nQEtUDAm7sYu%=zRssTg;;22lD7IGwyCXQks>-d87e)IBb@ z?`vnUymT=;s(~SCp>qd zwluE;gJA6M17W4fhk4X`DxPL86<@zogrR{!>BbrNXZ)%@oFLDo*@1#!abpuJ1IvXM zMZq8^yNRnOGcqu`7%c5M(!j)_(4?8FlBU_fAf+1&4uiF|V2T$S6udinjx>oWG|cFb zO_uNkhbuplp&1Jj7(uB;BZ9$!L2z$Ix=L;%BgcY^Z}mX}ycORhdo%?U8fLU*2`yIz zIjKnuSFpP=vm!}-nX^35!33m3#)PYbje*fbM~Hi2+?5x9)Jqv07GHs^iPEjo;gB-3Jq+5 z*LTj-n`o?|^!>~Q9~}lplL{g3iHsfV>y|}|CY>?hYG6=k(2#q#JdC+q#q~tMS8WFd z!-uaqA9`z?=x%qbc2WivWx^Y4i(=Nkyx3w?1S;nEPW3V^Vsv2GD4^x&c2J{%5tKe6 zAtCu;-dWExJ}L`CzbsDp!O+04Xh(`<>>m@5KEc`pZ#5)X85obac&&8gkyzW-rVon3 zqyCE%n@Oo9A1|NKp2!G}_J)aOSB#__7<$#FUA#0AWb5Ki zXrgdY7jtr-GO60+XaOq&qe<14m*6CEA;T1!#9)c#z|{*G20ctkp#0jiE!>T9BPa!} zUIOLt|9TE0U_LgbU+ai_8$*IuL=6bT} z?Iq4HTg`TCyZOj^tKjvQValbIYz_>DeSKg1nUWYC7<$bL+))zjg^VOgRu@neRWA7X}Chx!ySi7U%WFt?E(!n_4E5W)OL3s&g?^|9-Vt`vS zE2i4~*4kyd2kzc$IC=S49H{78p6L zA1pVt`XzhmM4?;OA4V2|NZvaSCwZ)p&1AkNXXxf@vO`Z&-Q`;04Mm8p7NwEvH5ay00$e ztI3n_>SP6Fs3b*OhR)uKm}k6KJ)|{2b;^%j$C$5AQ_y>)+54c;OX0*`@tEF=2RRs+ zI#MruDLZ$;o*`2<^T)0&&Vmdq7apu^W6L~n$x4uS=_FQ#h6pz=7T#qZ(q5b_lA!WO zm3yD$nTx4Y4~sLf2)L?pe_HANg8hCElhiaH<|&elOdU^-G{vo2Ab;6(!aPaVE#Tag zbzSoIu8xaG42>8Z7z}xrCFP0m zC(9K%*tC?HSuVUd3C-b)m3KfBX)Yv9F1R=en)|*yft2V9lU6G}VQI=u{jVXhk3pf~ z#4e|_ExE>E%laPlp`_>q7t!;Y8?+q9lPyYi)Pwz)0dZi~6&Zzw6Z=+8zfs-fe((dR z?)zFj*ZtrJP#JLRfb5AmR!3H4H!vhgkQr~&J;|FD^{0xY@a2ta&uXC6j zFt}AM$GSK{BtR5omaYkx+d+*5R-jmGN)_p83Unw1B?#8j8WOCF8(x75EbeK623&58 zS3%Jzro+rL;ro@qO<9wrwCzrsCYNk|so}uRz-SV+fyaR1?1_g~i)C(eB|5EcOJo&Y z$HYBfM(;Q0&gKZql4^JEB@eklY2Ac_ZQs$R^%oRF<*uLfdUDIL&#uI>=AFXPwTly8 zfErM-$BhuqT6Y1LQx+$P^f0XgB~aZ6w}ToItRWDG^fUz;aIN42)!zw{V86 zsKPE$of~0MlZwB+0hyif0c6GnG44-OyIQ!G1{!d+8D-?;$b2{@Jzc7%>g3sK21b|d zs(*ikeeYZMN0wDt*1*VY(#-X*K0Z_a!oAt^WcHK1?+l>I?XET`JQWyDu(GT>#5P;1 zrj^Zy$L+CJ$Olo26Tdt`ZJekLAm_DkNe4y!H86B)Wm@{_2B@w)$JL9+*$S)+3WHO8 zB5ajApIy$|CaTcj29B1t)P*h;ob0;k$&5@HB_GN}dYT-$JkEYU!OE)3mIAi$RuUo% zUQGH2k4xT+aE?k(`*p1rS9VAxi}W-#vhnirE(t%De(_Vc;lD2F__{-;%bL7-4PSj^ zY+z7YouHWdfc>vXSx$}9>A#t_7fiU?>?$PZ!ecY$9%WTT(*!j7qaSTX5{Zjn`cit)2Y4r;r%yev)tSm6Bz^? z7PE45vhuAp`Vp|Iv^^`obc@3ha8seF^~5{IeLSvjUAj-NyQsKgVz>fdC`7%LMf2_t z5_ytx*L_$_*@WExT5W>Zq9S5(#D|mbxsL@Gw5sM7OPYOY@zx(1iI$M&MAU|!2g^RL zh<9gP%izFpu~b-Tq3{uRP|`aLYW~EUD<)l0%24m<2SsHYsE6ZN_?O-A6DXxNEo5e3 z%J^b~lAAyhm`V5rw^+{dGrgA{~ zJHZW5TAd&{R|=f9A22g8njl5Hz)4na%l7EUQb(lr{sYDILrw;!j%TroHLU`_PW}ZM z(ex11272cGtJAWzzT`}rNKey4RtBbyHDaeTxItP18$kWH$k!2W(GnmHOf{e~S8k7N z?p9EG<|+UskzEOrdz~x6rHBp#hr%Lmu_wC|!NIebL7ss_VbU(A#mU-W?Vv_cuii^} zSoRdS_&UNZ{A3>};5t|t7+o$&aPv&Ql;t?z@z61_Djt1MEwgry>Qe2NeL3^5DLV`qL zXO7BU56h?u9v*Pn&FZgfE+q66lr(ig#R=a%a8QCv!IB=Fex7*g`BCG`##V2wxK@BW zJPDFXt<0ZF4A%YLvhk#H|Kf>@_-~t-*p=>h;(qW16QrCsba>xbmh^t2)LaG$))OF? zrne%>U!;V0Ap=q7F1Scs{Q?hpCV%sOw!@(ExnULqsQq&tTyk=)xD}O{k^=HH4Qd8- zS1K@E*-<+|>bU(Qt5uUCY8Sf)^f5_*>~2aG5Ybw2^}K@34I{Oct2eoa7xqJBW(%+v zK6BN}NO(0N|MTH;eM2+T6E!8DKZsVn$og=z_pDz@`QuNwH6-jn0T->)yyj`s+6M=x zMoxUnDH``8ZNY5!HIgkyHk^F8seDU-B&ccQ7Om5~BVeJccE$oDqis%{X4_1ncJSDP zk__u<1&xpmrJ@&}7N*+O+*OR3DH+>$XQOPAo}+WVuHa=>X;x=>1{Q(GFYd~NLpCdX z8Uv$=)iHQoi0)%>fMcom!Me%G5md9FLyeXDS76JDcFBiU%L=|cm=CI``9U2&ah(^f zte7W!Xmc# zMrW^mO50SO-vqEbF!cJFgR6#udljB}sr&*Ci@$S$s{IEucgJ6+T>q>g(aA97%|t`P zN|w25>iL1%`x#j-EYOc|3xC|#6zC8Y=NPxhKA@6Yp~3C?Mo@9e5vBfQmE!iC1z+WC zL49~|bL+!2wuenEmy8-_@U~rH0T&G*wJu!JLEHjK{6ULdU$es7DK&~MyH%Zh8Xdc* z@gTwhTs&=ol!53TQkW&Cv*X!C_w|24g=qz-i6){u?c$+SkshX3PvyT<@8Ni=AERC1 zDd-Mr_C46_^uzCkyl#Qp!46%WbBmq|Cx7}ZR#d0-wIMg@=tWRhHfBRW^*QOE8n!Xr z6>i)!0v#fyl>aNdYSeJ?2xQhSn9x7hW&Mneq6$xBHuUD0DgFhGo#icb@?ms7v$lQ7 z40BMM^uT>9#Vs0rQ)atg)UFQt1RC(Unxweh((R??ti=f_SDz>^3ouyb7v6E>l-z8& zEcp$*0uDM)#Do?nh+J5eAkDg@cjbcZ8XhN?U$IJ%1`VUS>Q1}Zlva`UYT>MhgKi4-19sTdPe@VT8 zh|prmJP{r>1_6iWeMgP1aQ9z4W6klX?||LxK!;ie0SA?>5pGAUrWsot@okG&G@SL& z4cuPp<-6Cn&897Mp#>Lc+;+jm;9u^-pE8!oLK^P7`cg##?#+^Z61)6j)1-f(;_*8d z@8mPiXL-y)1(pW5rv+-wPCW$evK+iEIETaC_>RT`QBd>3|E2o3DS2P6@#=lN_&EpE z@Tg-@XzaAcC=hAxZLuQ^J>D{h%U59yAawpDYP&8?iGmbu6oH zbz2Xv#8a3W7#96}_w3?pdr%cu3363ABonUy^)O1xKZA-JNKYvoxzc?K8pDEgp*-%j zSVUa_WezS5P$MK4oT}6TkhO?v0F?^hMoh?t1Iy;T zw0vuG>n|wZgNn5zsk3iD8kn+}8yJ*c-)On0cM9YPP#bFjqrF)o$Si|Dpk@HHyn#5i z@eW88*r^`#M5S~?Kt5t^0gW_u8Rvt7i}f%!Xl&tigh%ei178-*|6spsQaPwC2x>5{ zyL|S=TG=A*RiG}!!3S&%j4qd}TRB7>HZ*ljm<1NRz{J4lQWVZ1DZILUg3P|K3mM*^ zrsP6)2BwSy=8ApWJU~{0BIQNUh6UTL3VfscxA}p@8%Ue|5uL@?>tH_Q?Gihk@etI``2ZU8{!!0x?M(9Scg)joXh^UgX6#wAR&xQUf2z}b zQ1Hd{=?mSDImEoS3J5>hC$hh9%Lmy^28D(fw~`uJ4a~IN-?xBzRzJ2xFfetj(-o^> zJ)W@;Y&57#Z*|uOR5ieYa!b99(qHehw{6@R)xfoQYsb6W9;b4Yb@SXAb3wUMOebSz zmP*RH!2Nw|xRMnhLM|$2CI)Tp-Lh3|v98!HP%)t!;bwfh?Nq8N=N{u zH8W6KbHRe^{cHGczhF}Q+;P?4XQ|(r7o1D~DO~5hvWY)de&xT`o33X(&Vn_81|peLEeL~h6h3tCPoI7VvsP{1CpI`3=>5JN&&T3$g z%7yexL4IakoN&QB0yMwv1+HH}2|+`GwOI}plAz%Yu)>inG$C_?fss*@;VucY(v~M3 zso*XVI8+uxX7`PFr~Uu`|MJp67yfFfI4D~=NP$K_CcY|s(#F}*dncl?cauQYo&@RM z%!~{P-%x0NbXc%&U!93Sn|C4737WnGrFu}mN2G^I$|UbZ=7I$2 zNXPhX4eR=3Om9dT|B&+eAr)|B-PDC5=t;|jchUclVE46KHLpbQJNLSQx?A%=`2jRc z33iqvKd8`#49!67Xd*$V0QgsPiyshb!ZBEQA z7c!b#xf-ID{{T0PK#}-{m)q$p<5wyDAATED-FWx4aT*?f$-m7w8r3md6c2gS7klLL z2V`p-8}?2-SaCqcfuVO@BBWgi&Okngb6(6V;}&pOtPE~kfdy?8w(%`LK1CpBCgVQg zl+HloIa1o7*~-Pd;JOd&`-W4z(nV65OFndlI(OM@{c?c|w4z}OEM7t8u-Ss@EjDo7 z32r|caN!bC+~3xE!Swo$Clj7}-kom;Y6C!mlj#?z_jQR^EJmFGZ+_84`hteCJ zFZT$qV&~*#5m-D=h(W-?xr&$j)5RkOK3>dBKO>T#bf_&gYUzp>yixx=RL8Gw5x}aAj%;f9hMmFxCQkq*bWukmJ121J9FimpudE9=bjdkT>ThIuB%Vjq1b(+&IwjBUzNDv8F zZfMEHmjAfz(k|in#f;I%<-^_QUhv@E6!~MCr9hNIPX$)d35p5k}GSk*ld!YALVuZumGs@aotpBF)yfP-jOb}jKm*psg+$6jZ|fg_CvG-AE0XM=$Cbu%uvgC5y7JR!{a zOTHg-cUJ}_Ur>YA%{{{HV8-FZ25Elhg`4EoK6p7-%>8kONDos9sC@=Xj2BG9++K;@ zdKmGrC+#yRIvBTtW(sOrQbl-54R&2i++eQq;-a+t*;9*q4+k1>v4IwoTCqb$$^Pv2?kJhK;pI)}I?QdEL8l8Wo ztaIeeVL87}p4*=Lmb4sC0BKrr(L4L!gLj>L*M6v)^Rz|(lJl$iz_}*<-{N^ky4v}z z7;=|P4_};+0U95>IpxBOUsDsA&P|Yd@4Q&i@Dk(F?{guo6~?`w09$j==$3}$%mkBL zuqMw1CI%LP(-{I_Rct~5pO@VMc@sJh3Tp36J+C3r8*arE<-5N3*hLMAcc4D=dm$-S z=S2o`tjy6ndyl;a$NvIQ{f;P_=C*>u1DyRP=eZxuP_&R*4^5#j7#$dRe@@Q3DDABA zT=V`~)>~1Z9m!n*4{0tlm6!*5V#vf*yEwf3&@D12eU~5Fci|tJAtGe`0&YO5v zdD}+D==kO}?Q@QudU5dr)CMj$#=4J+;?IL=F3_*F_QCrv@G{I~y>xhEd&xPhcw!UB{|wwof? zco#s8;=OCZ(kjgffZ8m8U)HxsTXyNn9)569Fw1R7`Tf~L4N7LB#DxH2o+y*KAH!vtPv>n}+ zmJ`mvv&0f^;6aTAdJG&2v*PPIa!Xog^)eNmbAh6sX#91=T%(=N7whl?Qo;{q+Da=G687}?Vh!OLvi+XSaxbOQ}Cz&!2& znw$GBIPIdW1xyGOzo5kgFQ8>KB-SC7rYWL+KByr9O2gnx^6B7q?Fydrj9iAyNl)*bgwl8kI;aN zbI?i#9tIADT{36;?=+yLt(Bm}P!#?7g;YP1Q2`*M@@~&^cMpTu3JxF#a5U#;t1U$} z9Xua;7Bwr21aN`I2i!j^uTPcCy#Wq%h*1p@pd~e1wlUPE3z$l4-B>pN!`%N(I&M7G zkkA6F0D}FaAnbyZN}SmTj$FVpf*Gt35-a!}p8_w~y?u zs4s!@0;{ITEp>kxIBVM%4GG8y5+C=vBQ9@ODtbvdzvvas$#JT4_@@3Tv2yVjNCGu^ z`!dkr52)mOD z28|nDhSmyT&n!;Z04mtfI!AO73gEcV|Ll0F&wq{jDfXx2pZ0&sJT1?9SQ^|?Zgp~a zVfHHUA5`vgyFC|VsDh{_-$k0LQkeGejpY~T)(hX?9DcH~v;W(Q*N_G6o*X*85x*3Opbi0w+Oq?BBf(s$IQ&^+B@~);)T${`9s4+zUKzHYZzXG z)(1=a-dNEq8RQ;4;cDNMGe)nj2k!6tReWWA^VRi>6MleNuCXPj4Xk)UOIaYL2TvJ; zfWzT3ls@g@B^wW&I0358K+PURDhB0oH&C}9Q>a0yVb6u!l8d)L^xVH#A-k^;p>-iaRJ@AStg7zpho0*bx9mxJ_H(Bg`~V5QhKIa$nDb7T_Z=$&xaWXP>+Ls fYQWsUz|3$-U&W&2Z`)c11_lOCS3j3^P6b7+BIBeH|GXHuly04xG%uz@U)q5#-CjP^HSi(9q1l@bfEaktaVse$A>qe)11SyL}CIxzI6 zsmd@YG`KOdu`_TixY%%zk%6hBb&6A|3S$Gqp_xezjf{s6Ga49~OD#XVi;NbIP(sd6u21b*lQxZXp4h#?9Y9~tPNJU*W`Dw|Z z&=ApKTCyb7xPf7j7c>7g$KU_=&->DMM{x5{^k43ZeZk4SQT=8gBjB< z<%_rZCNMbUc0Rh~y=LzgE(ws%_p^k~^M)}9I8+4ltaW?P&II!7N3bW{ZW~%KI56~< zaTYK%Fi5=>dceTIXfi7a%+pNLkbtJn$ z0lFFNxy6jl%nU3SG7f-El`;UE!^Q*hH#Vt8_QUSYjG_!I0;ga6;8p{L*dM>S<)B~4%(1|6W-Xi5JO-wWEj|aC z7??6l0ycpB#4Do1pwKXb-DfkSM|VS81>qz#Nr9M6OL9$u?Dy=h#Hd{69Alp+$4qp$zW{|!*8GQC+BsMlm^vODdNMJ>mzhQ2;1nYv=~4zp zlU*&c+@OG*XbuVmH)d9D1`Y)^CuTk6jSE;g6x5QAOSlPv6ufC>1x3|@1V#o%6Df@d zP^w_#0tJYR{w+z*=mZ9)jFR8nAOYriwQR{;EF22U7S1}7$q5Sa1xGPL9Aejkiw?+< zVf#E&M1U6*Iqbbmix?dkcs-I7ue_GbTYUJ3+urs`@r+CznHyNtwq12P!e*goykTZ* zx9^(SCpOLxc%*R6ml>pW%UL6?4p5f4l_tsR!rZ_h<*T!}Ap?}BI#a=>cdYrkb;miq zhL6%xQf|)EjxE?X?_c&LMy8H6N1Af)^6UN6SYW4+W^JZqw_^GV2BwZwlNarF3$CAI zZ(vwdH9NKB_ss)L0UsWBGsQf1JaRGdw~T55h0wP) zHy@>6{)NZbI24+`H$T4R)OwFuN;i;g!z;^f^@$S6{nj~4xqf*Hz9{4paL~Epv2-zK zYJ)d#@}wGVfMAQj&Nc!E&iH z4K}qCWs|PIl?4TnPRp;yCCursJh=m6l#nSQl<= zk6D2`8v}>JCjQTkaV(~{4#-M^0z@y;?USoS$mx~NYh2hk7F;wt&ER$`;B*G4%!+XH zb_b=XUbShE@D!XI!6B<@b>;lprDh=e7W=w`gVhZ^z**L{xiu$>2rz@fZ_8dIt`1fP zMw2LBND76UcM%#tF5izh^*ZW0O}CwxeEfwG_aO#_h8Oc9F7$tP=&wl1%;{{t&>D6` zPQu4SzNf!uW2f-zIHz~gpzO!CB;{b4C(Ex}5;3=vKF;0L@p?z7(`n5PLG@#CPhJIlll88D2RRs+ItokH)QbpkgECj6H6*>m(uJGwX$=J_P;O1akpbXAIdM+J2i~py zO%Hh)m^z*;_T_x*B(rzb^lu3Y>t3=lHZUk^&*mvJ*09VV+*dJ6A<3F?xThT>j_XD>$dyI;)V!N{iuIMhIKK6Bq(TS zLyD4&k{$J6zjiEAV0<}iM$lg^Re?8d0Yz@aehp+w;iv)F|V#$T7bK6rbY!nwOAytxX_ z3h=uz2skvSO_tm_(bb3LVf1@P@r0&%9_?vKj2sIbH@h@mx#JeXWYDO(;^5T7?O7+8 z1RR=;HVVvkcE5e~3o8TT5u+QwK2;Ro&Uhpd05WNLN}`OeA)7TQ3spN`iL}4p+}2r~P{0IA1-7R%oTO%gYVPZsA(;@7?9HI5?ZrudX91oN zH&Di#JRed-xrKl42j^K(WI}S6H#GZQT&n=d-zwqGCpc4VHWnG-`fU$_NarK6S%h=)~h{VQH2JGciU1Ix>Rto>!v3& zGG&0msi(=2%j4|#6RfPdY$=|gcG|5Zgkdix{ey=QZ$@~>4Cn5Zg{8+eBv_ewrKF@h z*7uxW^i(|gkEr>+KP}lltkb2Et~_RDU~~yP=yd7;|F+5JH;QWg;F^E2d8Yfp0QQz| zTnojOj;v}FHM@}~sbG3up!zk7;ZHGzh7(#59(VH{bn3T!kYH67eURN7pd9}wdXvAO zoVp6XfWzYE?F1tTOEBP1(2yX3q=1WC6BJ9Det;5tLIpzu!=l$}E+q|hNFq`q*Xsq8 z?LpZ@!U|NX@mEP5WVjw{wqy>UJ(nA!86>-S=mng+A{_gG=j$Q1aR2Hp3w@-)65PF6 zg+=+>7VkM7>Ayv4PW0D}A{GT()vlBo&OP+-D7o7p_teMrpAdoKIi&KynJlGA^J7DtJLDH^(QS~WNI)sFeI^rC#Y;; zpJ;5gmo4UH1b94~VfsPO*HvUB3LcPw)Z+>QThNU*vvg37s#J=b!e1%|*$?fjFL zcciX+{$6~&9+XFH7#$cUZe23}ic{-_#=VPMPiaW79_D6X5jeZs>ByWz_PgfPi}W-- zWMyFLSR-~igF9KIrzsFz;htnY{o-jNIC`Bx4KlesvbkG9MsgK^S`)hxB=b-` zFmNa=;+{5fhejTlF9)g~cR4Li)&}cNVsK!%SR)HAu@atu^j689exY>}oP;+rC^UFT zB{wDpMd`jU_PFr_qy^%hH+Ro2S=B14u%v9U8~40G1FklZ!JEWROx)Dh8qnyJvH!x0 zigOwgtj?hJ!AV`QAY&`T>Q*=I2_UuNpfW}~3{)s!lo=Ey$zG&FX;JpUruV=4K|VS7 zfsujH<Q^a(3yip3zTX4sE&KnueI$6($))PSO#wbXk1!)l*iW%)HSe#%I=gwFM zNr>}Y+};K9J6~PD@Abe2ZU&YMFP44?FqkBC(_`1fqAedJB%n&8@>En#1@7-- ziURdJW%q!}DoBm-LINDl;FR+{K^0EVx!-*DhuHS5EJ(>gq2zxu2Vy|m*{qDAVai=t1NLABeB z`z;;%jf&|iGpwH6{`BGQ?`~gSBl){FHLY^Hq-x5NHw)jryW@|WPlVtnW4-#vT+%6) zNq4+@es1B4uE}!a*180$J!Tk+eVP1fqA<_8mL45{4d+F(8}sBgrU?igR%<^Umm|sa zH>Ea+J2U2;W1ghY?I$jVu(7Abi8Xqe!}R0f!jFXxPn~)3=?U503eP4Pez_)Oc$X!} zSYV-{WKD9!k6lVfw&fN4oOmm-d$qLB+pbI%h6aWtnZ3W0MR>l6J<^b<`Ut87UZd5_ z7dLicHdBa61}4xfvVsqi_|R%k;;W5bq&zBxtKp!=%Kb~xaz~%@k@6*vz%|SZCeWzB z!KSxQbe}Bdx#-WN{LN$|DBT^r0Lt877i)U%Ok@73QtIUHY-VE8bL^Ui#HI!N_SL!k z6jx|q`*J4wi}TElq6Qn9(wDfN(U7oPuyNzWXJrqp`6Xg|ZWPb|*eU!fk%8qxgZwhv zD!W%260A2qxhhAoM*9cJ9V%TeyiU7d!^~!1-Hm$%8yFUO9&~z9#Szmdubbd@FhkL4 zmQ)QZpXSf58`F3;I?FOJnpoYn0abekDt{~QoN5b-Shkm-Ho)AKuQgwBB}zLVyxOby z?U0pMy9PWwJ4DQu@Hht)cQnmeCkh%3Y{lBDA}aqh?@N%p`_+caO^HFZI>pCF&T;w4 zlhIdBDn2_h4wg-@9NfFZ|c&jU(<6&N>Kybo^MXI14<>j+j zOBLkg)Uk|}dtK$1i`n)d|K0;N6J5Jgw-iXSE^es$$^KVP-{~j!b*`A8BNrJR7iCqhi$^_h{6&E0S6Uvoh$P`yV-g1UssxUktt22 zXNgmbi0K9<_MbOAzjR4PGdi7PynP}~_!7T>gU%B%ts^1^NA6U4bX9`g`e&`MQ}-i} zos}YcH>s=EGqDIn>WUT3PM$Jvu_c$=!3{bhJu5GZDs9_zd|CS~_8evw0q&ixM?k5x zVP7k+;abt4&zf8LZn&{CFuDYGr*2sQE-w>6jl6G=Dj-4R0TZY(foL}hoMb(nkq9oa z6<9zi0=e{#L=>=su^SSi_dE4BrIlQL3#(r*vYvL>8g^m2Cb*{p>R>K#ybm#(#}*VG zTO-`AZv?jwIzXA_Ql$>0wXwJXTx+=p_rV*+C6LAdq>}oM++s#{(Nsj+QmOL8v>U;@ zE=HU0IN~n8AJoMD!{ER$F=WF6`5IqcgQy)Ki}!#g3V2Es4XXZv+QNtVL0PSTV^7?F zLoT<2A3$Mqd9M?+VPwPzYL%3Ke(_Zv6d^XCN@n+LL~~)+?n)L=??9$7)?UhDaf1>_ zfp$KUf=^{C+e6M>5xzS~^3+<1n8{s>PtGg@HD~U%Slr>Bb?}Dmfv3wJzGGX}`g7*R z9>08$0lL!?Lj)$w61=D{=bqwX^K3@I<)gh{IFB1IPIdzg`nr7E$f=yGFg>i5)im14 zf}1OgY1OrVA?H4*EtaeTjd3SQ{*kOISi5U!`t*)0)>h7MhO9Bq{+r7s{gZzCiN^{Q z5#_BL*3Mg+>$dFkS9yzBXRn;d2|4Sbb|TS5Lx4T~k@P0k8yX6rVW-wRXY9B#F0XRn zO}`~3_T|)##o*SNlx*_CP{yRD^$BuTYr7sYxN!&k0`Bn!9kC=?|K#AWn|i7ZZF z0SV5{Q@iyZG@{ePlmu!V=uMD@7upNvi%OZq&bw$-A_5&};JepXRtfFTDolExr?&hk z)8*#6L;qi7{#~3P!s8DbGnjkHKhLqD?$G~^7g3;yW&%ajmpDZyR)IAOl$NA{nwSP# zK(T*E)alrfSBfg_>qSj&O#)>A&=5e9R0ey`HV;rXuLCu^f;KGJZdEY%j{PkwkP=WY zLaFkDSY=x}*d(qEpcd)Qqeiz@Nr0R#0LnDGK9zwa!F}zN^^oRO4^s@N8JxGVXIm7= zaMm3R4h)99Y@kl*;)YwGuE%aCP*MW7Zj^oo{kXX90yx8hI+T<2TID$mcEy35zmOd? z^)!jcum4cnJ4iE~O9MROpT#Es_8%xIb8&zMGhR&jchL!?Bd~#&fkhzl^4Z0#pa%S6 z26+Y!g-z@JOS}~V1rgT=Xh#5^!!DeMcH-kp9FaDWQ_vP?HxAp%) z9{$1Hz_94&(hoQK=Cz0hoXAdgi*^LXg*=0R19O$8&8M&OJI=`Eo&>q-3nbJRsY&ep z2kIz*N2}j(pI#Aq0x87VmS~$mb0Dah`GT_u9xAx%Vxp>>#Ylr5Y_$iCb~S*y8=%0< zxFD{xxceE%hgE3tKs6hY+b_Hx2aT%Ug3B0?4;N%?>=Dxg1)4sCfJ4uJ_Jp%G+g{rh zbb{hW=77hoSxl$4g9h#-WE$%#C96_+j%;1WTsrlQ$H&(m-1jGf2J_eHibXIaZ!Y@c z_X5;=@$Y6;Xn1ie>0o$4kC+z7Xi$7CMyu$4A6LG#|H_`T#Ucry5ii|{ju-oSmz*)X z^r%ea0vouA-!yHdo6|F?udh$6GGJo@3pu$QlF>B1?Gc@Acq?h4EU2HU<83dyC z8}8bc4DX^iG1yMm?iCAJ42oFpX^PiA9dLgf8$!RwPU=C6DmLjk#2-rg`#U- zcJqW?7n3>Nz_S2NsavvL!lrj32|=9Bv}$`BXz&lYwgC_445DT$lk5z`T?fG(Yy(g$ zPlC01`$lMI$QE2zgA1Mn5uPeg#lj07H5+t7I*T0*zA(OEamYzf@H1%JnYzH=U+oEB z)^6dudkzZmUGa%tte(XGzr>o$?O+0UqAx*m8LNzkObe%|%ZpywoM#7)Y`p23)7s`Q zYx-uvmZYQ4*lM0l%$7GjbL^Uf>M`CWA$~s&HD5Vqyz^oEy0#l`ii(9vmlZS%9dzya zrj>Z-MD&`~xG~y->L#;@Hj4w{8WW=w4oX?>;L>#J<;a;Ts@W>7XZ@j5@HJnM7DLJJ z2}e(C<&Up;yt<)t<&ux4ejkr=?lW=9eCnz3=Y{O@DN7ua9@QKCm?-sfn#A_QQtk$y z-KDh`H^hMwr?k%Df-S964lzf{?z?AVC@O9D_lU@`BVU{}zq0SlTQIHm#V?Pq`bDXZ zS${6V(U`=uv1Yl&ml@y7}N+gt85%3WXFJ2lXO9~7P4dF`Igtk-TgCKhoO7IAq8 zeVP2qEa*$nEB>fo((~*9tm(4@IyX5N-k8yUCI6E7<#Zfa|t5h&|*oqvLLk;@uu4$&P?*0n2KwMlZ9U$L{T zRf<=z=#ui!DsuEwyQ5ZPHQZ1k7XBdSPV^P6SC&?PYVZ?>u3Ie zx`5e`E}%10k68$)7`_P#kn50Q1jJ`uoNxh@O>;-4kP)w#o12?~oA}{neG_Chz5(|s zL2ZKs5%3ZT=Vb>!{r_LT%cSc?;(3TTsJ`X$UY^~l-Ax5DCX2Fp0n7H zmzQ_($}CVD#ej>A`B#EIbmuqB%KEy>bzcQ%yF5&2eM=>zJIZJoi<% z8`Io}Ra|F|ttsvhR1W7|`EAlFvAuOmO4yhc8rn*lSG2cToSCvBUkB8&V*`&{C4fwEXg1MTCvrvRqA!(ii;f}<0j{U zTk4<`+7xK;0+RosDG_7`DMEU?mNH4~H7Eq@<8otM`julF^MeG2`Cpu9=PFsm&%nHAixgUlL%T6ZD=I}@G#lv_pYHfDKDTs}=M3RHv76@tYp z$eabB8I@a*xeBO#xP+cu-ypE-nbdTdO*SikUq%XHhj<172j^V}n_j0lgCd5(Ur9NETlQF-Q_Y0qGpQ_*6R;~^8i zwaZ#GGEE*x87eOO=$v)9>B>bp(6X~dna!-%MNVfpgVQr8kPGtf1VoiT$~H-O{p0bg z6~a<09nHA!8?igRcH7vyFJQAEvw%a1G-T8h5@?KtrK*$$W zsyJKJfW<;<3j=Ci+=~p)_l^apJIPX z{%QZG%=7ZBhou=<1g^SAblhyWog*Z$ve$Lmdpx z-^>gw0^DnL7aaH8e&U(n?Hdhi*E?Qw_DfFvk^ySOLyT$o2I_=vdNFN=8_!lL*Ci%a z8(u1xZejhK0iMGH83dZVkq3=c^lacUGqBllVC?VMS;LMATlLgF+7pE;w*a51uCrr+BzrLYItZ8w>u7`fF*|^W%bq=&Np7l&2 z!9>RG;0I8%O+i^_@nY@82`(mFY=uY9RZRQU+tkQ)exgu6%i{-UGUmxalE&TqY&qFj z$y|}17hbJl4{N4bWocZpzOrnlPg_)+lE}ZsPtR&dfClF>><%^M&i-6ry&`vupxb`7 zQvq`SmM=MI@POCwapH^n7rE+0dYTkLvsTa4@AkEvnsY(1(phT4jcm2$Nz%Ci0`^P} zYs>6x|5#1Ad?>?8hIMfQxQjf&6;$T%%x=2VGNZ;Jk4-Aqr^m%1?@o_diQB;f&{*1a z*X=j*Jmgpx_jWbr1q56e0tq^ z6Wt@-q_v!RCRoK9OYMc&YQUAjz`)dz*PWMm*?IA0Cui;+x62?qLHXezNNU~fS?ZZY4OzZ+GgJb$T+Z{a%NYS^JGZg)bMCQ$7Y(O_5vb{xWb@-4_js zf1o5@Rv@Fxu#LsrkC|=n#-?816ZtpFoO&-U{-R;))qm;BLid9oK&y6*rpWfs3z#S^ zal`M*(P`BXS>a>T3E{5CQ-d%rDX03Bzf@MWjSYJbcwaq za|?LM*25G8Dnd$IQnxHbD&CliK%!qC>;UBiXkP1q2)@nuchT((EGPpFxK4mdxbK3~ zF3MUUg_MFQXc~?K(K>+@ILP(0hQt>Jg@zUP5*+7!I=Eds!@@Mpt=t|_^n!ZTE??QW z4coW#Zs)4|l7lG18@7SM*-W+NebTCnD;FWTa{{OkPgB3UXnsNWT0>)mSDOMIco;Yo zcFCOWztbQo8;eLuZj38IQBf5A`Gr(Jl2PFLAn*1pclR)GC`0_`pa&|8bFR9LgS$Tb`WbO?_gg};4`E1$7P@67bDy?;6+58W4|2JVPE6UcL7L|V-|EAq zg38-2PP^Cxs+A$J&m+em;GpAt^J9yY$Pp6@Y$@(?h5(Y66E=X#MdUUpy}CC692feZ z9WV9y53N%BKV_bVRJ#{4np>S5UYNatSGSj;)h*WQ5=#wAQz32sJI`A}~WcA-T|$H0!6nc1~> z>czzip^eWE4Hx;I8|1EGcpU{=n&o?AMXzL#d-Q~>eN)aDy}BN_zwcM^mG#Y6*F#r@ z#g?2#tzpYR>%hyrG7tqH>)|D^ffi+?HaH}YyGdgeYEWv}b0N3n;_VMT_b*n+?yCfi zA1{P8#yhkwB#4Svv6@xYo&C^red3ngDNBxO|2lUJG}OlhUT&ly!Fq$s*y+)W+XpUg zZ|QGZD7${~M+=+Br6N5|3wfYTv;&NmtWuliU1Tc!aYWnnYk$+lKUQ3BjHaLl08)nn zPlrcN#vD0x??xo1q<~saf`exGsEu*Km$0V`3$#DKNUa5xQ#qg>RQk@r|N%zov!ho9tC{~Z;NyYa_;RXn(Wa|X4iUVuw&6Sf^%aWiDh zDrV6$#D1mUGTF%>YwTp`!8&lGLk_ZD$pX%j1q7wSny8AqN!C;v)Xf0+Qjle zO$QMk@Jdr~V&F+=W~_Y6v5#wd$k&!H!cqKAtE#eY_xsydS@f8J3s=zUWjB3$utsQ^ z@gQ#vgS7@|#ul^by#=YW25qFVdQ&MBb@0G66X#Q*>R literal 0 HcmV?d00001 diff --git a/doc/perf_3o.png b/doc/perf_3o.png new file mode 100644 index 0000000000000000000000000000000000000000..198d3639b3ad0e0c09b52b55b7aeac01325cad4c GIT binary patch literal 13258 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%0D}v)twPegh@khoD}2z?KL%?&+X5 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 tKO7c9jr#U7ylqgAgA2>cJ*l_>= literal 0 HcmV?d00001 diff --git a/doc/performance.html b/doc/performance.html new file mode 100644 index 0000000..6ad7dbe --- /dev/null +++ b/doc/performance.html @@ -0,0 +1,724 @@ + + + + + +Boost.MultiIndex Documentation - Performance + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Performance

+ +
+ +
+ +
+ +

Contents

+ + + +

Introduction

+ +

+Boost.MultiIndex helps the programmer to avoid the manual construction of cumbersome +compositions of containers when multiindexing capabilities are needed. Furthermore, +it does so in an efficient manner, both in terms of space and time consumption. The +space savings stem from the compact representation of the underlying data structures, +requiring a single node per element. As for time efficiency, Boost.MultiIndex +intensively uses metaprogramming techniques producing very tight implementations +of member functions which take care of the elementary operations for each index: +for multi_index_containers with two or more indices, the running time +can be reduced to half as long as with manual simulations involving several +STL containers. +

+ +

Manual simulation of a multi_index_container

+ +

+The section of simulation +of standard containers with multi_index_container shows the equivalence +between single-index multi_index_containers and some STL containers. Let us now +concentrate on the problem of simulating a multi_index_container with two +or more indices with a suitable combination of standard containers. +

+ +

+Consider the following instantiation of multi_index_container: +

+ +
+typedef multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    ordered_non_unique<identity<int>, std::greater >,
+  >
+> indexed_t;
+
+ +

+indexed_t maintains two internal indices on elements of type +int. In order to simulate this data structure resorting only to +standard STL containers, one can use on a first approach the following types: +

+ +
+// dereferencing compare predicate
+template<typename Iterator,typename Compare>
+struct it_compare
+{
+  bool operator()(const Iterator& x,const Iterator& y)const
+  {
+    return comp(*x,*y);
+  }
+
+private:
+  Compare comp;
+};
+
+typedef std::set<int>  manual_t1; // equivalent to indexed_t's index #0
+typedef std::multiset<
+  const int*,
+  it_compare<
+    const int*,
+    std::greater<int>
+  >
+>                      manual_t2; // equivalent to indexed_t's index #1
+
+ +

+where manual_t1 is the "base" container that holds +the actual elements, and manual_t2 stores pointers to +elements of manual_t1. This scheme turns out to be quite +inefficient, though: while insertion into the data structure is simple enough: +

+ +
+manual_t1 c1;
+manual_t2 c2;
+
+// insert the element 5
+manual_t1::iterator=c1.insert(5).first;
+c2.insert(&*t1);
+
+ +deletion, on the other hand, necessitates a logarithmic search, whereas +indexed_t deletes in constant time: + +
+// remove the element pointed to by it2
+manual_t2::iterator it2=...;
+c1.erase(*it2); // watch out! performs in logarithmic time
+c2.erase(it1); 
+
+ +

+The right approach consists of feeding the second container not with +raw pointers, but with elements of type manual_t1::iterator: +

+ +
+typedef std::set<int>  manual_t1; // equivalent to indexed_t's index #0
+typedef std::multiset<
+  manual_t1::iterator,
+  it_compare<
+    manual_t1::iterator,
+    std::greater<int>
+  >
+>                      manual_t2; // equivalent to indexed_t's index #1
+
+ +

+Now, insertion and deletion can be performed with complexity bounds +equivalent to those of indexed_t: +

+ +
+manual_t1 c1;
+manual_t2 c2;
+
+// insert the element 5
+manual_t1::iterator=c1.insert(5).first;
+c2.insert(t1);
+
+// remove the element pointed to by it2
+manual_t2::iterator it2=...;
+c1.erase(*it2); // OK: constant time
+c2.erase(it1); 
+
+ +

+The construction can be extended in a straightworward manner to +handle more than two indices. In what follows, we will compare +instantiations of multi_index_container against this sort of +manual simulations. +

+ +

Spatial efficiency

+ +

+The gain in space consumption of multi_index_container with +respect to its manual simulations is amenable to a very simple +theoretical analysis. For simplicity, we will ignore alignment +issues (which in general play in favor of multi_index_container.) +

+ +

+Nodes of a multi_index_container with N indices hold the value +of the element plus N headers containing linking information for +each index. Thus the node size is +

+ +
+SI = e + h0 + ··· + +hN-1, where
+e = size of the element,
+hi = size of the i-th header. +
+ +

+On the other hand, the manual simulation allocates N nodes per +element, the first holding the elements themselves and the rest +storing iterators to the "base" container. In practice, an iterator +merely holds a raw pointer to the node it is associated to, so its size +is independent of the type of the elements. Suming all contributions, +the space allocated per element in a manual simulation is +

+ +
+SM = (e + h0) + +(p + h1) + ··· + +(p + hN-1) = +SI + (N-1)p, where
+p = size of a pointer.
+
+ +

+The relative amount of memory taken up by multi_index_container +with respect to its manual simulation is just +SI / SM, which can be expressed +then as: +

+ +
+SI / SM = +SI / (SI + (N-1)p). +
+ +

+The formula shows that multi_index_container is more efficient +with regard to memory consumption as the number of indices grow. +

+ +

+These considerations have overlooked an aspect of the greatest practical +importance: the fact that multi_index_container allocates a single +node per element, compared to the many nodes of different sizes +built by manual simulations, diminishes memory fragmentation, which +can show up in more usable memory available and better performance. +

+ +

Time efficiency

+ +

+From the point of view of computational complexity (i.e. big-O +characterization), multi_index_container and its corresponding manual +simulations are equivalent: inserting an element into +a multi_index_container reduces to a simple combination of +elementary insertion operations on each of the indices, and +similarly for deletion. Hence, the most we can expect is a reduction +(or increase) of execution time by a roughly constant factor. As we +will see later, the reduction can be very significative for +multi_index_containers with two or more indices. +

+ +

In the special case of multi_index_containers with only one index, +the best we can hope for is equal performance: the tests show that the +performance degradation in this particular situation ranges from negligible +to small, depending on the compiler used. +

+ +

Performance tests

+ +

+See source code used for measurements. +

+In order to assess the efficiency of multi_index_container, the following +basic algorithm +

+ +
+multi_index_container<...> c;
+for(int i=0;i<n;++i)c.insert(i);
+for(iterator it=c.begin();it!=c.end();)c.erase(it++);
+
+ +

+has been measured for different instantiations of multi_index_container +at values of n 1,000, 10,000 and 100,000, +and its execution time compared with that of the equivalent algorithm +for the corresponding manual simulation of the data structure based on +STL containers. The following compilers have been used: +

    +
  • GNU GCC 3.3.1 for Cygwin 1.5.7,
  • +
  • Intel C++ Compiler for Windows 32-bit 7.1,
  • +
  • Microsoft Visual C++ 6.0 Service Pack 5,
  • +
+with their default release settings. All tests were performed on a Wintel +box equipped with a P4 1.5GHz processor and 256 MB RAM, running +Microsoft Windows 2000 Professional SP2. +

+ +

+The relative memory consumption (i.e. the amount of memory allocated +by a multi_index_container with respect to its manual simulation) +is determined by dividing the size of a multi_index_container node +by the sum of node sizes of all the containers integrating the +simulating data structure. +

+ +

Results for 1 ordered index

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >
+  >
+>
+
+ +

+which is functionally equivalent to std::set<int>. +

+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
100%100%100%
+Table 1: Relative memory consumption of multi_index_container with 1 +ordered index. +

+ +

+The figures confirm that in this case multi_index_container nodes are the +same size than those of its std::set counterpart. +

+ +

Execution time

+ +

+performance of multi_index_container with 1 ordered index
+Fig. 1: Performance of multi_index_container with 1 ordered index. +

+ +

+As expected, multi_index_container does perform in this case somewhat +worse than std::set. The degradation is within 10% for ICC and +MSVC compilers, while in GCC peaks to 20%, which can be significative +in certain applications. This latter result is presumably accounted for by +a lower quality of the optimizing stage carried out by GCC. +

+ +

Results for 1 sequenced index

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    sequenced<>
+  >
+>
+
+ +

+which is functionally equivalent to std::list<int>. +

+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
100%100%100%
+Table 2: Relative memory consumption of multi_index_container with 1 +sequenced index. +

+ +

+The figures confirm that in this case multi_index_container nodes are the +same size than those of its std::list counterpart. +

+ +

Execution time

+ +

+performance of multi_index_container with 1 sequenced index
+Fig. 2: Performance of multi_index_container with 1 sequenced index. +

+ +

+As in the former case, multi_index_container does not attain the performance +of its STL counterpart. Again, worst results are those of GCC, with a +degradation of up to 20% , while ICC and MSVC do not exceed a mere 5%. +

+ +

Results for 2 ordered indices

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    ordered_non_unique<identity<int> >
+  >
+>
+
+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
90%90%90%
+Table 3: Relative memory consumption of multi_index_container with 2 +ordered indices. +

+ +

+These results concinde with the theoretical formula for +SI=36 and p=4. +

+ +

Execution time

+ +

+performance of multi_index_container with 2 ordered indices
+Fig. 3: Performance of multi_index_container with 2 ordered indices. +

+ +

+The experimental results confirm our hypothesis that multi_index_container +provides an improvement on execution time by an approximately constant factor, +which in this case ranges from 65% to 75% depending on the compiler. +

+ +

Results for 1 ordered index + 1 sequenced index

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    sequenced<>
+  >
+>
+
+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
87.5%87.5%87.5%
+Table 4: Relative memory consumption of multi_index_container with 1 +ordered index + 1 sequenced index. +

+ +

+These results concinde with the theoretical formula for +SI=28 and p=4. +

+ +

Execution time

+ +

+
+Fig. 4: Performance of multi_index_container with 1 ordered index ++ 1 sequenced index. +

+ +

+For n=103 and n=104, the results +are in agreement with our theoretical analysis, showing a constant factor +improvement of 60-75% with respect to the STL-based manual simulation. +Curiously enough, this speedup gets even higher when +n=105 for two of he compilers (35% for ICC, +25% for MSVC.) In order to rule out spurious results, the tests +have been run many times, yielding similar outcoumes. A tentative +explanation of this unexpected behavior may point to a degradation in +the execution time of the manual simulation, attributable to poor +performance of the standard STL allocator in ICC and MSVC when dealing +with many objects of diverse sizes (the manual simulation is comprised of +an std::set and a std::list, which demand +differently sized nodes.) +

+ +

Results for 3 ordered indices

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    ordered_non_unique<identity<int> >,
+    ordered_non_unique<identity<int> >
+  >
+>
+
+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
86.7%86.7%86.7%
+Table 5: Relative memory consumption of multi_index_container with 3 +ordered indices. +

+ +

+These results concinde with the theoretical formula for +SI=52 and p=4. + +

+ +

Execution time

+ +

+performance of multi_index_container with 3 ordered indices
+Fig. 5: Performance of multi_index_container with 3 ordered indices. +

+ +

+Execution time for this case is between 55% and 65% lower than achieved with +an STL-based manual simulation of the same data structure. +

+ +

Results for 2 ordered indices + 1 sequenced index

+ +

+The following instantiation of multi_index_container was tested: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    ordered_unique<identity<int> >,
+    ordered_non_unique<identity<int> >,
+    sequenced<>
+  >
+>
+
+ +

Memory consumption

+ +

+ + + + + + + + + + + +
GCC 3.1.1ICC 7.1MSVC 6.5
84.6%84.6%84.6%
+Table 6: Relative memory consumption of multi_index_container with 2 +ordered indices + 1 sequenced index. +

+ +

+These results concinde with the theoretical formula for +SI=44 and p=4. +

+ +

Execution time

+ +

+
+Fig. 6: Performance of multi_index_container with 2 ordered indices ++ 1 sequenced index. +

+ +

+In accordance to the expectations, execution time is improved by a fairly constant +factor, which ranges from 45% to 55%. +

+ +

Conclusions

+ +

+We have shown that multi_index_container outperforms, both in space and +time efficiency, equivalent data structures obtained from the manual +combination of STL containers. This improvement gets larger when the number +of indices increase. +

+ +

+In the special case of replacing standard containers with single-indexed +multi_index_containers, the programmer should balance the benefits brought on +by Boost.MultiIndex (subobject searching, in-place updating, etc.) against the +resulting degradation in execution time. Depending on the compiler, this degradation +can reach up to 20% of the original time. +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/prev.gif b/doc/prev.gif new file mode 100644 index 0000000000000000000000000000000000000000..c35dfeec25d2397b8746300b0bf6a62caacd728e GIT binary patch literal 852 zcmZ?wbhEHb6krfw_|5>L~%oSd9oTwL7T+&nxy zyu7@8e0==;`~m_3f`WoVLPEmA!XhFfqN1W=Vq)Ur;t~=Pl9G~AQc}{=(lRnKva+&r za&q$W@(KzHii(O#N=nMg$|@=KYmvnwpwgT3XuL+B!Nqy1Kf0dV2c$ z`UVCDhK7blMn=ZQ#wI2vrlzK5W@hH*<`xzfmX?-QR#w*5);2aawzjr*c6Rpm_6`mX zj*gB_PEO9w&Mq!4uCA_bZf@@G?j9ZO-;?s%`Gi0t*xzX zZEfxC?HwH*ot>RsU0vPX-90@$y}iACeSQ7?{Szikm^g9bq)C$|Po6ww%9N>7r%szT zZTj@-GiJ<~IdkT$S+i!(o;_#IoVj!7&YL%H{`~n17A#n}aN(jwixw|lykyCerAwDC zTefWZ^5rX5tXR2n<*HSyRoH%*%du|Ns9wpd0|o3k)2V z4F5T0JSHeGFt7^$v#tnu=+MR^>(nx%;E_wSn7-bb6B`#F?`N`itKrzRz`avaw(ZJ_ NqU6KVRG66 + + + + +Boost.MultiIndex Documentation - Reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Reference

+ + + +
+ +
+ +

Contents

+ + + +

Header dependencies

+ +

+The following dependencies among headers of Boost.MultiIndex hold: +

+So, a program using Boost.MultiIndex must include + +"boost/multi_index_container.hpp", +the headers defining the index types to be used and possibly one or more key +extraction headers for key-based indices. Note that all the key extractors +provided by Boost.MultiIndex are automatically included with + +"boost/multi_index/key_extractors.hpp". +

+ +

+Boost.MultiIndex is a header-only library, requiring no linking with additional +object modules. +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/reference/indices.html b/doc/reference/indices.html new file mode 100644 index 0000000..e27da56 --- /dev/null +++ b/doc/reference/indices.html @@ -0,0 +1,328 @@ + + + + + +Boost.MultiIndex Documentation - Index reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Index reference

+ + + +
+ +
+ +

Contents

+ + + +

Index concepts

+ +

+multi_index_container instantiations comprise one or more indices +specified at compile time. Each index allows read/write access to the elements +contained in a definite manner. For instance, +ordered indices +provide a set-like interface to the elements, whereas +sequenced indices mimic the functionality +of std::list. +

+ +

+Indices are not isolated objects, and so cannot be constructed on their +own. Rather they are embedded into a multi_index_container as specified by +means of an index specifier. The name of +the index class implementation proper is never directly exposed to the user, who +has only access to the associated index specifier. +

+ +

+Insertion and erasing of elements are always performed through the +appropriate interface of some index of the multi_index_container; +these operations, however, do have an impact on all other indices as +well: for instance, insertion through a given index may fail because +there exists another index which bans the operation in order to preserve +its invariant (like uniqueness of elements.) This circumstance, rather +than an obstacle, yields much of the power of Boost.MultiIndex: +equivalent constructions based on manual composition of standard +containers would have to add a fair amount of code in order to +globally preserve the invariants of each container while guaranteeing +that all of them are synchronized. The global operations performed +in a joint manner among the various indices can be reduced to +six primitives: +

    +
  • Copying,
  • +
  • insertion of an element,
  • +
  • hinted insertion, where a preexisting element is suggested in + order to improve the efficiency of the operation,
  • +
  • deletion of an element,
  • +
  • replacement of the value of an element, + which may trigger the rearrangement of this element in one or + more indices, or the banning of the replacement,
  • +
  • modification of an element, and its subsequent + rearrangement/banning by the various indices. +
+The last two primitives deserve some further explanation: in order to +guarantee the invariants associated to each index (e.g. some definite +ordering,) elements of a multi_index_container are not mutable. +To overcome this restriction, indices expose member functions +for updating and modifying, which allow for the mutation of elements +in a controlled fasion. Immutability of elements does not significantly +impact the interface of ordered indices, as it is based upon that of +std::set and std:multiset, and these containers +also have non-mutable elements; but it may come as a surprise when dealing +with sequenced indices, which are designed upon the functionality provided +by std::list. +

+ +

+These global operations are not directly exposed to the user, but rather +they are wrapped as appropriate by each index (for instance, ordered indices +provide a set-like suite of insertion member functions, whereas sequenced +indices do have push_back and push_front +operations.) Boost.MultiIndex poses no particular conditions on +the interface of indices, save that they must model + +Container (without the requirement of being + +Assignable.) +

+ +

Complexity signature

+ +

+Some member functions of an index interface are implemented by +global primitives from the list above. Complexity of these operations +thus depends on all indices of a given multi_index_container, not just +the currently used index. +

+ +

+In order to establish complexity estimates, an index is characterized +by its complexity signature, consisting of the following +associated functions on the number of elements: +

    +
  • c(n): copying, +
  • i(n): insertion, +
  • h(n): hinted insertion, +
  • d(n): deletion, +
  • r(n): replacement, +
  • m(n): modifying. +
+ +

+Each function yields the complexity estimate of the contribution of the index +to the corresponding global primitive. Let us consider +an instantiation of multi_index_container +with N indices labelled 0,...,N-1 +whose complexity signatures are +(ci,ii,hi,di,ri,mi); +the insertion of an element in such a set is then of complexity +O(I0(n)+···+IN-1(n)) where n +is the number of elements. To abbreviate notation, we adopt the +following definitions: +
    +
  • C(n)=c0(n)+···+cN-1(n),
  • +
  • I(n)=i0(n)+···+iN-1(n),
  • +
  • H(n)=h0(n)+···+hN-1(n),
  • +
  • D(n)=d0(n)+···+dN-1(n),
  • +
  • R(n)=r0(n)+···+rN-1(n),
  • +
  • M(n)=m0(n)+···+mN-1(n).
  • +
+For instance, consider a multi_index_container with two ordered indices, +for which i(n)=log(n), and a sequenced index with i(n)=1 +(constant time insertion). Insertion of an element into this multi_index_container +is then of complexity +
+O(I(n))=O(2*log(n)+1)=O(log(n)). +
+

+ +

Index specification

+ +

+Index specifiers are passed as instantiation arguments to +multi_index_container and provide the information needed to incorporate +the corresponding indices. Future releases of Boost.MultiIndex may allow for +specification of user-defined indices. Meanwhile, the requirements for an index +specifier remain implementation defined. Currently, Boost.MultiIndex provides the +index specifiers +ordered_unique and +ordered_non_unique for +ordered indices and +sequenced for +sequenced indices. +

+ +

+Header + +"boost/multi_index/indexed_by.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<typename T0,...,typename Tn>
+struct indexed_by;
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Template class indexed_by

+ +

+indexed_by is a + +MPL Forward Sequence meant to be used to specify a +compile-time list of indices as the IndexSpecifierList of +multi_index_container. +

+ +
+template<typename T0,...,typename Tn>
+struct indexed_by;
+
+ +

+Each user-provided element of indexed_list must be an index +specifier. At least an element must be provided. The maximum number of elements +of an indexed_by sequence is implementation defined. +

+ +

Tags

+ +

+Tags are just conventional types used as mnemonics for indices of an +multi_index_container, as for instance in member function get. +Each index can have none, one or more tags associated. The way tags are assigned +to a given index is dependent on the particular index specifier. However, +for convenience all indices of Boost.MultiIndex support tagging through the +class template tag. +

+ +

+Header + +"boost/multi_index/tag.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<typename T0,...,typename Tn>
+struct tag;
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Template class tag

+ +

+tag is a typelist construct used to specify a compile-time +sequence of tags to be assigned to an index in instantiation time. +

+ +
+template<typename T0,...,typename Tn>
+struct tag;
+
+ +

+Elements of tag can be any type, though the user is expected +to provide classes with mnemonic names. Duplicate elements are not allowed. +The maximum number of elements of a tag instantiation is +implementation defined. +

+ +

Indices provided by Boost.MultiIndex

+ + +

Key-based indices

+ +

+Indices of this type are organized around keys obtained from the +elements, as described in the key extraction +reference. +

    +
  • Ordered indices sort the elements + on the key and provide fast lookup capabilites.
  • +
  • Hashed indices (not currently implemented) offer high + efficiency access through hashing techniques.
  • +
+

+ +

Other types

+ +

+

+

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/reference/key_extraction.html b/doc/reference/key_extraction.html new file mode 100644 index 0000000..01560ec --- /dev/null +++ b/doc/reference/key_extraction.html @@ -0,0 +1,1505 @@ + + + + + +Boost.MultiIndex Documentation - Key extraction reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Key extraction reference

+ + + +
+ +
+ +

Contents

+ + + +

Key Extractors

+ +

+Key extraction classes are used by ordered indices to +obtain the sorting keys from the elements of a multi_index_container. +An Assignable +class KeyFromValue is said to be a key extractor from a +type T if +

    +
  1. the type KeyFromValue::result_type is defined,
  2. +
  3. k1(ca) is defined and returns a value convertible + to const KeyFromValue::result_type&,
  4. +
  5. if k2 is a copy of k1, k1(ca) is the + same value as k2(ca),
  6. +
+for every k1, k2 of type const KeyFromValue, +and ca of type const Type&. +

+ +

+Additionally, KeyFromValue is a read/write key extractor +if the following extra conditions are met: +

    +
  1. k1(a) is defined and returns a value convertible + to KeyFromValue::result_type&,
  2. +
  3. const_cast<const KeyFromValue::result_type&>(k1(a)) + is the same value as + k1(const_cast<const Type&>(a)),
  4. +
+for every k1 of type const KeyFromValue and +a of type Type&. +

+ +

+Boost.MultiIndex provides five general-purpose key extractors: +

+plus replacements for some of them: + +that workaround some deficiencies in the support for non-type template parameters +by certain compilers. +

+ +

Chained pointers

+ +

+The key extractors provided by Boost.MultiIndex are templatized according +to the type Type and serve to extract keys not only from objects +of type Type, but also from reference wrappers provided by +Boost.Ref and from chained pointers +to Type (or to reference wrappers of Type): a chained pointer +is any type P such that, for an object p of type +const P +

    +
  • *x yields an object of type Type& or + boost::reference_wrapper<Type>, OR
  • +
  • *x yields a chained pointer to Type&,
  • +
+that is, chained pointers are arbitrary compositions of pointer-like objects +ultimately dereferencing to values of Type& or +boost::reference_wrapper<Type>. +

+ +

Header + +"boost/multi_index/key_extractors.hpp" synopsis +

+ +
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/mem_fun.hpp>
+#include <boost/multi_index/composite_key.hpp>
+
+ +

+This header includes all the key extractors provided by Boost.MultiIndex. +

+ +

+Header + +"boost/multi_index/identity.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<typename T> struct identity;
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Template class identity

+ +

+identity is a Key Extractor +that acts as a do-nothing identity functor. +

+ +
+template<typename Type>
+struct identity
+{
+  typedef Type result_type;
+
+  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;
+  const Type& operator()(const Type& x)const; 
+  Type&       operator()(Type& x)const; // only provided if Type is non-const
+
+  // only provided if Type is non-const
+  const Type& operator()(const reference_wrapper<const Type>& x)const; 
+
+  // only provided if Type is const
+  Type& operator()(
+    const reference_wrapper<typename remove_const<Type>::type>& x)const; 
+
+  Type& operator()(const reference_wrapper<Type>& x)const;
+};
+
+ +

+identity<Type> is a model of: +

+

+ +

identity members

+ +template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const; + +
+Requires: ChainedPtr is a chained pointer +type to Type.
+Returns: a reference to the object chained-pointed to by x. +
+ +const Type& operator()(const Type&x)const; + +
+Returns: x. +
+ +Type& operator()(Type &x)const; + +
+Returns: x. +
+ +const Type& operator()(const reference_wrapper<const Type>& x)const; +
+Returns: x.get(). +
+ +Type& operator()(const reference_wrapper<typename remove_const<Type>::type>& x)const; +
+Returns: x.get(). +
+ +Type& operator()(const reference_wrapper<Type>& x)const; +
+Returns: x.get(). +
+ + +

+Header + +"boost/multi_index/member.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<class Class,typename Type,Type Class::*PtrToMember>
+struct member;
+
+template<class Class,typename Type,std::size_t OffsetOfMember>
+struct member_offset;
+
+#define BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName) implementation defined
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Template class member

+ +

+member is a Key Extractor +aimed at accessing a given member of a class. +

+ +
+template<class Class,typename Type,Type Class::*PtrToMember>
+struct member
+{
+  typedef Type result_type;
+
+  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;
+  const Type& operator()(const Class& x)const;
+  Type&       operator()(Class& x)const; // only provided if Type is non-const
+  const Type& operator()(const reference_wrapper<const Class>& x)const;
+  Type&       operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

+The PtrToMember template argument specifies the particular +Type Class::* pointer to the member to be extracted. +member<Class,Type,PtrToMember> is a model of: +

+

+ +

member members

+ +template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const; + +
+Requires: ChainedPtr is a chained pointer +type to Type.
+Returns: a reference to the object chained-pointed to by x. +
+ +const Type& operator()(const Class&x)const; + +
+Returns: x.*PtrToMember. +
+ +Type& operator()(const Class&x); + +
+Returns: x.*PtrToMember. +
+ +const Type& operator()(const reference_wrapper<const Class>& x)const; + +
+Returns: x.get().*PtrToMember. +
+ +Type& operator()(const reference_wrapper<Class>& x)const; + +
+Returns: x.get().*PtrToMember. +
+ +

Template class member_offset

+ +

+Some compilers do not properly support pointers to members as non-type +template arguments. The following have been confirmed to have bugs in +this respect: +

+In this situation, member_offset provides an +alternative to member accepting offsets +instead of pointers to members. Please note that the use of +offsetof on non-POD types is forbidden by the standard; +luckily enough, most compilers accept it nevertheless, so +member_offset serves as a workaround for most practical purposes. +

+ +
+template<class Class,typename Type,Type Class::*PtrToMember>
+struct member_offset
+{
+  typedef Type result_type;
+
+  template<typename ChainedPtr> Type& operator()(const ChainedPtr& x)const;
+  const Type& operator()(const Class& x)const; 
+  Type&       operator()(Class& x)const; // only provided if Type is non-const
+  const Type& operator()(const reference_wrapper<const Class>& x)const;
+  Type&       operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

As an example of use, given the class

+ +
+class A
+{
+  int x;
+}
+
+ +

+the instantiation member<A,int,&A::x> can be simulated then +as member_offset<A,int,offsetof(A,x)>. +

+ +

Macro BOOST_MULTI_INDEX_MEMBER

+ +
+BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName)
+
+ +

+This macro is provided as an aid for using member and +member_offset when writing cross-platform code. In the usual cases, +it expands to +

+ +
+::boost::multi_index::member<Class,Type,&Class::MemberName>
+
+ +

+but it resolves to +

+ +
+::boost::multi_index::member_offset<Class,Type,offsetof(Class,MemberName)>
+
+ +

+for the following compilers: +

    +
  • MSVC++ 6.0 or lower, +
  • Intel C++ 7.1 or lower for Windows, +
+and/or if the macro BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS +is defined. +

+ +

+Header + +"boost/multi_index/mem_fun.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
+struct const_mem_fun;
+
+template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()>
+struct mem_fun;
+
+template<
+  class Class,typename Type,
+  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
+>
+struct const_mem_fun_explicit;
+
+template<
+  class Class,typename Type,
+  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
+>
+struct mem_fun_explicit;
+
+#define BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName) \
+implementation defined
+#define BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName) \
+implementation defined
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Template class const_mem_fun

+ +

+const_mem_fun is a Key Extractor +returning as key the result of invoking a given constant member function of a class. +

+ +
+template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()const>
+struct const_mem_fun
+{
+  typedef typename remove_reference<Type>::type result_type;
+
+  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
+  Type operator()(const Class& x)const;
+  Type operator()(const reference_wrapper<const Class>& x)const;
+  Type operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

+The PtrToMemberFunction template argument specifies the particular +Type (Class::*PtrToMemberFunction)()const pointer to the the constant +member function used in the extraction. +const_mem_fun<Class,Type,PtrToMemberFunction> is a model of: +

+

+ +

const_mem_fun members

+ +template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const; + +
+Requires: ChainedPtr is a chained pointer +type to Type.
+Returns: (y.*PtrToMemberFunction)(), where y is the +object chained-pointed to by x. +
+ +Type operator()(const Class& x)const; + +
+Returns: (x.*PtrToMemberFunction)(). +
+ +Type operator()(const reference_wrapper<const Class>& x)const; + +
+Returns: (x.get().*PtrToMemberFunction)(). +
+ +Type operator()(const reference_wrapper<Class>& x)const; + +
+Returns: (x.get().*PtrToMemberFunction)(). +
+ +

Template class mem_fun

+ +

+mem_fun is a Key Extractor +returning as key the result of invoking a given member function of a class. +

+ +
+template<class Class,typename Type,Type (Class::*PtrToMemberFunction)()>
+struct mem_fun
+{
+  typedef typename remove_reference<Type>::type result_type;
+
+  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
+  Type operator()(Class& x)const;
+  Type operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

+The PtrToMemberFunction template argument specifies the particular +Type (Class::*PtrToMemberFunction)() pointer to the the member +function used in the extraction. +mem_fun<Class,Type,PtrToMemberFunction> is a model of: +

+

+ +

mem_fun members

+ +template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const; + +
+Requires: ChainedPtr is a chained pointer +type to Type.
+Returns: (y.*PtrToMemberFunction)(), where y is the +object chained-pointed to by x. +
+ +Type operator()(Class& x)const; + +
+Returns: (x.*PtrToMemberFunction)(). +
+ +Type operator()(const reference_wrapper<Class>& x)const; + +
+Returns: (x.get().*PtrToMemberFunction)(). +
+ +

Template class const_mem_fun_explicit

+ +

+MSVC++ 6.0 do not properly support pointers to constant member functions as non-type +template parameters, thus const_mem_fun cannot be +used in this compiler. A simple workaround consists in specifying the type of +these pointers as an additional template parameter. +

+ +
+template<
+  class Class,typename Type,
+  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
+>
+struct const_mem_fun_explicit
+{
+  typedef typename remove_reference<Type>::type result_type;
+
+  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
+  Type operator()(const Class& x)const;
+  Type operator()(const reference_wrapper<const Class>& x)const;
+  Type operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

+const_mem_fun_explicit provides the very same functionality as +its const_mem_fun analogous instantiation. For example, given the type +

+ +
+struct A
+{
+  int f()const;
+};
+
+ +

+the extractor const_mem_fun<A,int,&A::f> can be replaced by +const_mem_fun_explicit<A,int,int (A::*)()const,&A::f>. +

+ +

Template class mem_fun_explicit

+ +

+For analogy with const_mem_fun_explicit, +a variation of mem_fun is provided accepting +an additional parameter with the type of the pointer to non-constant member function +used for extraction. +

+ +
+template<
+  class Class,typename Type,
+  typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction
+>
+struct mem_fun_explicit
+{
+  typedef typename remove_reference<Type>::type result_type;
+
+  template<typename ChainedPtr> Type operator()(const ChainedPtr& x)const;
+  Type operator()(Class& x)const;
+  Type operator()(const reference_wrapper<Class>& x)const;
+};
+
+ +

Macro +BOOST_MULTI_INDEX_CONST_MEM_FUN

+ +
+BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName)
+
+ +

+Use this macro when writing cross-platform code selectively using +const_mem_fun_explicit in place of const_mem_fun for +compilers not supporting the latter. In the usual cases, the macro expands to +

+ +
+::boost::multi_index::const_mem_fun<Class,Type,&Class::MemberFunName>
+
+ +

+but it resolves to +

+ +
+::boost::multi_index::const_mem_fun_explicit<
+  Class,Type,Type (Class::*)()const,&Class::MemberFunName
+>
+
+ +

+for MSVC++ 6.0 or lower. +

+ + +

Macro +BOOST_MULTI_INDEX_MEM_FUN

+ +
+BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName)
+
+ +

+By default, the macro expands to +

+ +
+::boost::multi_index::mem_fun<Class,Type,&Class::MemberFunName>
+
+ +

+but it resolves to +

+ +
+::boost::multi_index::mem_fun_explicit<
+  Class,Type,Type (Class::*)(),&Class::MemberFunName
+>
+
+ +

+for MSVC++ 6.0 or lower. +

+ +

+Header + +"boost/multi_index/composite_key.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<typename Value,typename KeyFromValue0,...,typename KeyFromValuen>
+struct composite_key;
+
+template<typename CompositeKey>
+struct composite_key_result;
+  
+// comparison for composite_key_result:
+
+// OP is any of =,<,!=,>,>=,<=
+
+template<typename CompositeKey1,typename CompositeKey2>
+bool operator OP(
+  const composite_key_result<CompositeKey1>& x,
+  const composite_key_result<CompositeKey2>& y);
+
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator OP(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y);
+
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator OP(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y);
+
+template<typename Compare0,...,typename Comparen>
+struct composite_key_compare;
+  
+template<typename CompositeKeyResult>
+struct composite_key_result_less;
+
+template<typename CompositeKeyResult>
+struct composite_key_result_greater;
+
+} // namespace boost::multi_index
+
+} // namespace boost
+
+namespace std{
+
+template<typename CompositeKey>
+struct less<boost::multi_index::composite_key_result<CompositeKey> >;
+
+template<typename CompositeKey>
+struct greater<boost::multi_index::composite_key_result<CompositeKey> >;
+
+} // namespace std
+
+ +

Template class composite_key

+ +

+composite_key is a Key Extractor +returning the combined value of several key extractors whose type is specified +at compile time. The returned object is of type + +composite_key_result<composite_key>. +

+ +
+template<typename Value,typename KeyFromValue0,...,typename KeyFromValuen>
+struct composite_key
+{
+  typedef tuple<KeyFromValue0,...,KeyFromValuen> key_extractor_tuple;
+  typedef Value                                  value_type;
+  typedef composite_key_result<composite_key>    result_type;
+
+  composite_key(
+    const KeyFromValue0& k0=KeyFromValue0(),
+    ...
+    const KeyFromValuen& kn=KeyFromValuen());
+
+  composite_key(const key_extractor_tuple& x);
+
+  const key_extractor_tuple& key_extractors()const;
+  key_extractor_tuple&       key_extractors()
+
+  template<typename ChainedPtr>
+  result_type operator()(const ChainedPtr& x)const;
+  
+  result_type operator()(const value_type& x)const;
+  result_type operator()(const reference_wrapper<const value_type>& x)const;
+  result_type operator()(const reference_wrapper<value_type>& x)const;
+};
+
+ +

+KeyFromvalue0, ... , KeyFromvaluen are the types of +the key extractors combined into the composite key. Each of these types +must be a Key Extractor from +Value. At least a key extractor must be provided. The maximum +number of key extractors of a composite_key instantiation is +implementation defined. composite_key internally stores an +object of every constituent key extractor type. +composite_key<Value,KeyFromValue0,...,KeyFromValuen> is a model +of: +

+

+ +

composite_key members

+ +composite_key(
+  const KeyFromValue0& k0=KeyFromValue0(),
+  ...
+  const KeyFromValuen& kn=KeyFromValuen()); +
+ +
+Effects: Constructs a composite_key that stores +copies of the key extractor objects supplied. +
+ +composite_key(const key_extractor_tuple& x); + +
+Effects: Constructs a composite_key that stores +copies of the key extractor objects supplied in x. +
+ +const key_extractor_tuple& key_extractors()const; + +
+Returns: a constant reference to a tuple holding the +key extractors internally stored by the composite_key. +
+ +key_extractor_tuple& key_extractors(); + +
+Returns: a reference to a tuple holding the +key extractors internally stored by the composite_key. +
+ +template<typename ChainedPtr>
+result_type operator()(const ChainedPtr& x)const;
+ +
+Requires: ChainedPtr is a chained pointer +type to result_type.
+Returns: a result_type object dependent on +*this and y, where y is the +object chained-pointed to by x. +
+ +result_type operator()(const value_type& x)const; + +
+Returns: a result_type object dependent on +*this and x. +
+ +result_type operator()(const reference_wrapper<const value_type>& x)const; + +
+Returns: a result_type object dependent on +*this and x.get(). +
+ +result_type operator()(const reference_wrapper<value_type>& x)const; + +
+Returns: a result_type object dependent on +*this and x.get(). +
+ +

Template class +composite_key_result

+ +

+This is an opaque type returned by composite_key +instantiations as their extracted key. +

+ +
+template<typename CompositeKey>
+struct composite_key_result
+{
+  no public interface available
+};
+
+// comparison:
+  
+// OP is any of =,<,!=,>,>=,<=
+
+template<typename CompositeKey1,typename CompositeKey2>
+bool operator OP(
+  const composite_key_result<CompositeKey1>& x,
+  const composite_key_result<CompositeKey2>& y);
+
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator OP(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y);
+
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator OP(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y);
+
+ +CompositeKey is the composite_key instantiation to +which the composite_key_result type is associated. Objects of type +composite_key_result returned by a composite key must be always treated +as temporary, i.e. they should not be stored or copied. +composite_key_result is not guaranteed to be a model of + +Default Constructible or +Assignable. +Every object of type composite_key_result<CompositeKey> is +internally asociated to the CompositeKey from which it is returned +and the object of type CompositeKey::result_type to which the +composite key was applied. +

+ +

Notation

+ +

+Given an x of type composite_key_result<CompositeKey>, +we use the following notation: +

    +
  • ck(x) is the CompositeKey object associated to + x,
  • +
  • v(x) is the object of type CompositeKey::value_type + associated to x,
  • +
  • ki(x) = ck(x).key_extractors().get<i>(), + that is, is the i-th key extractor of ck(x),
  • +
  • xi = ki(x)(v(x)), that is, the + key extracted from v(x) by the i-th key extractor,
  • +
  • length(x) is the number of key extractors of ck(x).
  • +
+Also, if y is a tuple of values, we define: +
    +
  • yi=y.get<i>(),
  • +
  • length(y) is the number of elements of y.
  • +
+

+ +

Comparison operators

+ +template<typename CompositeKey1,typename CompositeKey2>
+bool operator==(
+  const composite_key_result<CompositeKey1>& x,
+  const composite_key_result<CompositeKey2>& y);
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator==(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y);
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator==(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y); +
+ +
+Requires: length(x)==length(y). The expression +xi==yi is valid for all i +in [0,length(x)).
+Returns: true if and only if +
+xi==yi for all i +in [0,length(x)). +
+Complexity: No more key extraction operations and comparisons +are performed than those necessary for the evaluation of the expression above, +starting at i==0. The evaluation is short-circuited as soon as +the result is determined to be false. +
+ +template<typename CompositeKey1,typename CompositeKey2>
+bool operator<(
+  const composite_key_result<CompositeKey1>& x,
+  const composite_key_result<CompositeKey2>& y);
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator<(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y);
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator<(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y); +
+ +
+Requires: The expressions +xi<yi and +yi<xi are valid for all i +in [0,min(length(x),length(y))).
+Returns: true if and only if there exists some +j in the range [0,min(length(x),length(y))) +such that +
+!(xi<yi) && !(yi<xi) +for all i in [0,j),
+  xj<yj. +
+Complexity: No more key extraction operations and comparisons +are performed than those necessary for the evaluation of the expression above, +starting at i==0. The evaluation is short-circuited as soon as +the result is determined to be false. +
+ +template<typename CompositeKey1,typename CompositeKey2>
+bool operator OP(
+  const composite_key_result<CompositeKey1>& x,
+  const composite_key_result<CompositeKey2>& y);
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator OP(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y);
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator OP(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y); +
+ +

+(OP is any of !=, >, +>=, <=.) +

+ +
+Requires: The expressions given below are valid (for the particular +OP considered.)
+Returns: true if and only if +
+!(x==y) (OP is !=),
+  y< x  (OP is ),
+!(x< y) (OP is >=),
+!(y< x) (OP is <=). +
+
+ +

Template class +composite_key_compare

+ +

+composite_key_compare compares composite_key_result +instantiations between them and with tuples of values using an internally stored +collection of elementary comparison predicates. +

+ +
+template<typename Compare0,...,typename Comparen>
+struct composite_key_compare
+{
+  typedef tuple<Compare0,...,Comparen> key_comp_tuple;
+
+  composite_key_compare(
+    const Compare0& co=Compare0(),
+    ...
+    const Comparen& cn=Comparen());
+
+  composite_key_compare(const key_comp_tuple& x);
+
+  const key_comp_tuple& key_comps()const{return *this;}
+  key_comp_tuple&       key_comps(){return *this;}
+
+  template<typename CompositeKey1,typename CompositeKey2>
+  bool operator()(
+    const composite_key_result<CompositeKey1> & x,
+    const composite_key_result<CompositeKey2> & y)const;
+  
+  template<typename CompositeKey,typename Value0,...,typename Valuen>
+  bool operator()(
+    const composite_key_result<CompositeKey>& x,
+    const tuple<Value0,...,Valuen>& y)const;
+
+  template<typename Value0,...,typename Valuen,typename CompositeKey>
+  bool operator()(
+    const tuple<Value0,...,Valuen>& x,
+    const composite_key_result<CompositeKey>& y)const;
+};
+
+ +

+Compare0, ... , Compare0 are the types of the comparison +predicates stored by composite_key_compare. Each of these types +must be a +Binary Predicate. At least a +comparison predicate must be provided. The maximum number of comparison predicates of +a composite_key_compare instantiation is implementation defined. +composite_key_compare is +Assignable. +It is also + +Default Constructible +if each Comparei is + +Default Constructible. +

+ +

+Note that formally it is not required that the Comparei types +behave as comparison predicates in any definite way. However, the +semantics of composite_key_compare are well defined if this +is the case, as explained in the ordering +semantics section. +

+ +

Notation

+ +

+In what follows we use the same notation +introduced for composite_key_result. + +

composite_key_compare members

+ +composite_key_compare(
+  const Compare0& c0=Compare0(),
+  ...
+  const Comparen& kn=Comparen()); +
+ +
+Effects: Constructs a composite_key_compare that stores +copies of the comparison predicates supplied. +
+ +composite_key_compare(const key_comp_tuple& x); + +
+Effects: Constructs a composite_key_compare that stores +copies of the comparison predicate objects supplied in x. +
+ +const key_comp_tuple& key_comps()const; + +
+Returns: a constant reference to a tuple holding the +comparison predicate objects internally stored by the +composite_key_compare. +
+ +key_comp_tuple& key_comps(); + +
+Returns: a reference to a tuple holding the +comparison predicate objects internally stored by the +composite_key_compare. +
+ + +template<typename CompositeKey1,typename CompositeKey2>
+bool operator()(
+  const composite_key_result<CompositeKey1> & x,
+  const composite_key_result<CompositeKey2> & y)const;
+template<typename CompositeKey,typename Value0,...,typename Valuen>
+bool operator()(
+  const composite_key_result<CompositeKey>& x,
+  const tuple<Value0,...,Valuen>& y)const;
+template<typename Value0,...,typename Valuen,typename CompositeKey>
+bool operator()(
+  const tuple<Value0,...,Valuen>& x,
+  const composite_key_result<CompositeKey>& y)const;
+
+ +
+Requires: The expressions +key_comps().get<i>()(xi,yi) and +key_comps().get<i>()(yi,xi) +are valid for all i +in [0,min(length(x),length(y))).
+Returns: true if and only if there exists some +j in the range [0,min(length(x),length(y))) +such that +
+!key_comps().get<i>()(xi,yi) && !key_comps().get<i>()(yi,xi) +for all i in [0,j),
+ key_comps().get<j>()(xj,yj). +
+Complexity: No more key extraction operations and comparisons +are performed than those necessary for the evaluation of the expression above, +starting at i==0. The evaluation is short-circuited as soon as +the result is determined to be false. +
+ +

Template class +composite_key_result_less

+ +

+composite_key_result acts as a particularization of +composite_key_compare where all the comparison predicates supplied +are instantiations of std::less. +

+ +
+template<typename CompositeKeyResult>
+struct composite_key_result_less
+{
+  typedef CompositeKeyResult  first_argument_type;
+  typedef first_argument_type second_argument_type;
+  typedef bool                result_type;
+
+  template<typename CompositeKey1,typename CompositeKey2>
+  bool operator()(
+    const composite_key_result<CompositeKey1> & x,
+    const composite_key_result<CompositeKey2> & y)const;
+  
+  template<typename CompositeKey,typename Value0,...,typename Valuen>
+  bool operator()(
+    const composite_key_result<CompositeKey>& x,
+    const tuple<Value0,...,Valuen>& y)const;
+
+  template<typename Value0,...,typename Valuen,typename CompositeKey>
+  bool operator()(
+    const tuple<Value0,...,Valuen>& x,
+    const composite_key_result<CompositeKey>& y)const;
+};
+
+ +

+CompositeKeyResult must be an instantiation of +composite_key_result for some type +composite_key<KeyFromValue0,...,KeyFromValuen>. +composite_key_result_less<CompositeKeyResult>::operator() is +then equivalent to +composite_key_compare<Compare0,...,Comparen>::operator(), taking +

+Comparei = std::less<KeyFromValuei::result_type> for all +i = 0,...,n. +
+

+ +

+In addition to the requirements on Comparei imposed by +composite_key_compare, each of these types must be + +Default Constructible. composite_key_result_less +is +Default Constructible and +Assignable. +

+ +

Template class +composite_key_result_greater

+ +

+composite_key_result acts as a particularization of +composite_key_compare where all the comparison predicates supplied +are instantiations of std::greater. +

+ +
+template<typename CompositeKeyResult>
+struct composite_key_result_greater
+{
+  typedef CompositeKeyResult  first_argument_type;
+  typedef first_argument_type second_argument_type;
+  typedef bool                result_type;
+
+  template<typename CompositeKey1,typename CompositeKey2>
+  bool operator()(
+    const composite_key_result<CompositeKey1> & x,
+    const composite_key_result<CompositeKey2> & y)const;
+  
+  template<typename CompositeKey,typename Value0,...,typename Valuen>
+  bool operator()(
+    const composite_key_result<CompositeKey>& x,
+    const tuple<Value0,...,Valuen>& y)const;
+
+  template<typename Value0,...,typename Valuen,typename CompositeKey>
+  bool operator()(
+    const tuple<Value0,...,Valuen>& x,
+    const composite_key_result<CompositeKey>& y)const;
+};
+
+ +

+CompositeKeyResult must be an instantiation of +composite_key_result for some type +composite_key<KeyFromValue0,...,KeyFromValuen>. +composite_key_result_greater<CompositeKeyResult>::operator() is +then equivalent to +composite_key_compare<Compare0,...,Comparen>::operator(), taking +

+Comparei = std::greater<KeyFromValuei::result_type> for all +i = 0,...,n. +
+

+ +

+In addition to the requirements on Comparei imposed by +composite_key_compare, each of these types must be + +Default Constructible. composite_key_result_greater +is +Default Constructible and +Assignable. +

+ +

Specialization of std::less for +composite_key results

+ +

+std::less<CompositeKeyResult>, for CompositeKeyResult +being an instantiation of composite_key_result, has the same interface +and functionality that composite_key_result_less<CompositeKeyResult>. +

+ +
+namespace std{
+
+template<typename CompositeKey>
+struct less<boost::multi_index::composite_key_result<CompositeKey> >
+{
+  typedef CompositeKeyResult  first_argument_type;
+  typedef first_argument_type second_argument_type;
+  typedef bool                result_type;
+
+  template<typename CompositeKey1,typename CompositeKey2>
+  bool operator()(
+    const composite_key_result<CompositeKey1> & x,
+    const composite_key_result<CompositeKey2> & y)const;
+  
+  template<typename CompositeKey,typename Value0,...,typename Valuen>
+  bool operator()(
+    const composite_key_result<CompositeKey>& x,
+    const tuple<Value0,...,Valuen>& y)const;
+
+  template<typename Value0,...,typename Valuen,typename CompositeKey>
+  bool operator()(
+    const tuple<Value0,...,Valuen>& x,
+    const composite_key_result<CompositeKey>& y)const;
+};
+
+} // namespace std
+
+ +

Specialization of std::greater for +composite_key results

+ +

+std::greater<CompositeKeyResult>, for CompositeKeyResult +being an instantiation of composite_key_result, has the same interface +and functionality that composite_key_result_greater<CompositeKeyResult>. +

+ +
+namespace std{
+
+template<typename CompositeKey>
+struct greater<boost::multi_index::composite_key_result<CompositeKey> >
+{
+  typedef CompositeKeyResult  first_argument_type;
+  typedef first_argument_type second_argument_type;
+  typedef bool                result_type;
+
+  template<typename CompositeKey1,typename CompositeKey2>
+  bool operator()(
+    const composite_key_result<CompositeKey1> & x,
+    const composite_key_result<CompositeKey2> & y)const;
+  
+  template<typename CompositeKey,typename Value0,...,typename Valuen>
+  bool operator()(
+    const composite_key_result<CompositeKey>& x,
+    const tuple<Value0,...,Valuen>& y)const;
+
+  template<typename Value0,...,typename Valuen,typename CompositeKey>
+  bool operator()(
+    const tuple<Value0,...,Valuen>& x,
+    const composite_key_result<CompositeKey>& y)const;
+};
+
+} // namespace std
+
+ +

Ordering semantics

+ +

+Consider an instantiation of composite_key_compare with +types Compare0, ... , Comparen such that each +Comparei is a +Strict +Weak Ordering on a certain type Ti. Then the following +properties hold. +

+ +

+Let CompositeKey be a type of the form +composite_key<Value,KeyFromValue0,...,KeyFromValuej>, +with j <= n, such that +

+KeyFromValuei::result_type = Ti, for all i = 0,...,j. +
+Then, composite_key_compare is a +Strict +Weak Ordering on elements of type +composite_key_result<CompositeKey>, and the order induced +is lexicographical. Also, the following types are +Compatible Keys of +composite_key_compare with respect to +composite_key_result<CompositeKey>: +
+tuple<Q0,...,Qk>, k <= n
+composite_key_result<composite_key<K0,...,Kk> >, with +Ki::result_type = Qi for all i = 0,...,k. +
+provided that each Qi is either Ti or a +Compatible Key +of Comparei. In this case, the comparison is done +lexicographically only on the first 1+min(j,k) elements. +

+ +

+The rationale of this design is simple: +composite_key_results are regarded as "virtual" tuples +with each element being the result of the corresponding elementary key +extractor. Accordingly, these objects and actual tuples are compared +lexicographically taking the minimum length of the operands considered. +

+ +

+Analogous properties hold for +composite_key_result::operator<. As for +operator==, the semantics is compatible with that of +less-than comparison, with the additional constraint that only objects +of the same length can be tested for equality. In this regard, +the equivalence classes induced by x==y are subsets +of those associated to !(x<y)&&!(y<x). +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/reference/multi_index_container.html b/doc/reference/multi_index_container.html new file mode 100644 index 0000000..85ca13b --- /dev/null +++ b/doc/reference/multi_index_container.html @@ -0,0 +1,831 @@ + + + + + +Boost.MultiIndex Documentation - multi_index_container reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex +multi_index_container reference

+ + + +
+ +
+ +

Contents

+ + + +

+Header +"boost/multi_index_container_fwd.hpp" +synopsis +

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<
+  typename Value,
+  typename IndexSpecifierList=indexed_by<ordered_unique<identity<Value> > >,
+  typename Allocator=std::allocator<Value> >
+class multi_index_container;
+
+} // namespace boost::multi_index 
+
+using multi_index::multi_index_container;
+
+} // namespace boost
+
+ +

+multi_index_container_fwd.hpp forward declares the class template +multi_index_container and specifies its default parameters. +

+ +

+Header +"boost/multi_index_container.hpp" +synopsis +

+ +
+namespace boost{
+
+namespace multi_index{
+
+template<typename Value,typename IndexSpecifierList,typename Allocator>
+class multi_index_container;
+
+// multi_index_container associated global class templates:
+
+template<typename MultiIndexContainer,int N> struct nth_index;
+template<typename MultiIndexContainer,typename Tag> struct index;
+template<typename MultiIndexContainer,int N> struct nth_index_iterator;
+template<typename MultiIndexContainer,int N> struct nth_index_const_iterator;
+template<typename MultiIndexContainer,typename Tag> struct index_iterator;
+template<typename MultiIndexContainer,typename Tag> struct index_const_iterator;
+
+// multi_index_container global functions for index retrieval:
+
+template<
+  int N,typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type&
+get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);
+
+template<
+  int N,typename Value,typename IndexSpecifierList,typename Allocator
+>
+const typename nth_index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type&
+get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);
+
+template<
+  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type&
+get(multi_index_container<Value,IndexSpecifierList,Allocator>& m);
+
+template<
+  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
+>
+const typename index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type&
+get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m);
+
+// multi_index_container global functions for projection of iterators:
+
+template<
+  int N,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type
+project(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it);
+
+template<
+  int N,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index_const_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type
+project(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it);
+
+template<
+  typename Tag,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type
+project(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it);
+
+template<
+  typename Tag,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index_const_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type
+project(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it);
+
+// comparison:
+
+// OP is any of ==,<,!=,>,>=,<=
+
+template<
+  typename Value1,typename IndexSpecifierList1,typename Allocator1,
+  typename Value2,typename IndexSpecifierList2,typename Allocator2
+>
+bool operator OP(
+  const multi_index_container<Value1,IndexSpecifierList1,Allocator1>& x,
+  const multi_index_container<Value2,IndexSpecifierList2,Allocator2>& y);
+
+// specialized algorithms:
+
+template<typename Value,typename IndexSpecifierList,typename Allocator>
+void swap(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& x,
+  multi_index_container<Value,IndexSpecifierList,Allocator>& y);
+
+} // namespace boost::multi_index 
+
+using multi_index::multi_index_container;
+using multi_index::get;
+using multi_index::project;
+
+} // namespace boost
+
+ +

+Template class multi_index_container +

+ +

+This is the main component of Boost.MultiIndex. A multi_index_container +is a container class template holding a compile-time user-defined list of +indices. These indices provide different interfaces +for the management of the elements of the multi_index_container. By itself, +multi_index_container only provides basic functionality for construction +and for access to the indices held. +

+ +

+A multi_index_container type is instantiated with the type of the +elements contained and a non-empty + +MPL Forward Sequence specifying which indices conform the +class. +

+ +

+For convenience of use, all public methods and types of the first index +specified are inherited by multi_index_container. This also includes global +operators and functions associated with the index (vg. comparison and +swap.) +

+ +
+template<
+  typename Value,
+  typename IndexSpecifierList=indexed_by<ordered_unique<identity<Value> > >,
+  typename Allocator=std::allocator<Value> >
+class multi_index_container
+{
+public:
+
+  // types:
+
+  typedef implementation defined   ctor_args_list;
+  typedef implementation defined   index_specifier_type_list;
+  typedef implementation defined   index_type_list;
+  typedef implementation defined   iterator_type_list;
+  typedef implementation defined   const_iterator_type_list;
+  typedef Allocator                allocator_type;
+
+  // nested class templates:
+
+  template<int N>
+  struct nth_index                {typedef implementation defined type;};
+  template<typename Tag>
+  struct index                    {typedef implementation defined type;};
+  template<int N>
+  struct nth_index_iterator       {typedef implementation defined type;};
+  template<int N>
+  struct nth_index_const_iterator {typedef implementation defined type;};
+  template<typename Tag>
+  struct index_iterator           {typedef implementation defined type;};
+  template<typename Tag>
+  struct index_const_iterator     {typedef implementation defined type;};
+
+  // construct/copy/destroy:
+
+  explicit multi_index_container(
+    const ctor_args_list& args_list=ctor_args_list(),
+    const allocator_type& al=allocator_type());
+  template<typename InputIterator>
+  multi_index_container(
+    InputIterator first,InputIterator last,
+    const ctor_args_list& args_list=ctor_args_list(),
+    const allocator_type& al=allocator_type());
+  multi_index_container(
+    const multi_index_container<Value,IndexSpecifierList,Allocator>& x);
+
+  ~multi_index_container();
+
+  multi_index_container<Value,IndexSpecifierList,Allocator>& operator=(
+    const multi_index_container<Value,IndexSpecifierList,Allocator>& x);
+
+  allocator_type get_allocator()const;
+
+  // retrieval of indices
+
+  template<int N> typename nth_index<N>::type& get();
+  template<int N> const typename nth_index<N>::type& get()const;
+  template<typename Tag> typename index<Tag>::type& get()
+  template<typename Tag> const typename index<Tag>::type& get()const;
+
+  // projection of iterators
+
+  template<int N,typename IteratorType>
+    typename nth_index_iterator<N>::type project(IteratorType it);
+  template<int N,typename IteratorType>
+    typename nth_index_const_iterator<N>::type project(IteratorType it)const;
+  template<typename Tag,typename IteratorType>
+    typename index_iterator<Tag>::type project(IteratorType it);
+  template<typename Tag,typename IteratorType>
+    typename index_const_iterator<Tag>::type project(IteratorType it)const;
+};
+
+// multi_index_container associated global class templates:
+
+template<typename MultiIndexContainer,int N> struct nth_index
+{
+  typedef typename MultiIndexContainer::nth_index<N>::type type;
+};
+
+template<typename MultiIndexContainer,typename Tag> struct index
+{
+  typedef typename MultiIndexContainer::index<Tag>::type type;
+};
+
+template<typename MultiIndexContainer,int N> struct nth_index_iterator
+{
+  typedef typename MultiIndexContainer::nth_index_iterator<N>::type type;
+};
+
+template<typename MultiIndexContainer,int N> struct nth_index_const_iterator
+{
+  typedef typename MultiIndexContainer::nth_index_const_iterator<N>::type type;
+};
+
+template<typename MultiIndexContainer,typename Tag> struct index_iterator
+{
+  typedef typename MultiIndexContainer::index_iterator<Tag>::type type;
+};
+
+template<typename MultiIndexContainer,typename Tag> struct index_const_iterator
+{
+  typedef typename MultiIndexContainer::index_const_iterator<Tag>::type type;
+};
+
+// multi_index_container global functions for index retrieval:
+
+template<
+  int N,typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type&
+get(multi_index_container<Value,IndexSpecifierList,Allocator>& m)
+{
+  return m.get<N>();
+}
+
+template<
+  int N,typename Value,typename IndexSpecifierList,typename Allocator
+>
+const typename nth_index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type&
+get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m)
+{
+  return m.get<N>();
+}
+
+template<
+  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type&
+get(multi_index_container<Value,IndexSpecifierList,Allocator>& m)
+{
+  return m.get<Tag>();
+}
+
+template<
+  typename Tag,typename Value,typename IndexSpecifierList,typename Allocator
+>
+const typename index<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type&
+get(const multi_index_container<Value,IndexSpecifierList,Allocator>& m)
+{
+  return m.get<Tag>();
+}
+
+// multi_index_container global functions for projection of iterators:
+
+template<
+  int N,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type
+project(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it)
+{
+  return m.project<N>(it);
+}
+
+template<
+  int N,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename nth_index_const_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,N
+>::type
+project(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it)
+{
+  return m.project<N>(it);
+}
+
+template<
+  typename Tag,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type
+project(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it)
+{
+  return m.project<Tag>(it);
+}
+
+template<
+  typename Tag,typename IteratorType,
+  typename Value,typename IndexSpecifierList,typename Allocator
+>
+typename index_const_iterator<
+  multi_index_container<Value,IndexSpecifierList,Allocator>,Tag
+>::type
+project(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& m,
+  IteratorType it)
+{
+  return m.project<Tag>(it);
+}
+
+// comparison:
+
+// OP is any of ==,<,!=,>,>=,<=
+
+template<
+  typename Value1,typename IndexSpecifierList1,typename Allocator1,
+  typename Value2,typename IndexSpecifierList2,typename Allocator2
+>
+bool operator OP(
+  const multi_index_container<Value1,IndexSpecifierList1,Allocator1>& x,
+  const multi_index_container<Value2,IndexSpecifierList2,Allocator2>& y)
+  {
+    return get<0>(x) OP get<0>(y);
+  }
+
+// specialized algorithms:
+
+template<typename Value,typename IndexSpecifierList,typename Allocator>
+void swap(
+  multi_index_container<Value,IndexSpecifierList,Allocator>& x,
+  multi_index_container<Value,IndexSpecifierList,Allocator>& y)
+  {
+    x.swap(y);
+  }
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Complexity

+ +

+In the descriptions of operations of multi_index_container, we adopt the +scheme outlined in the +complexity signature section. +

+ +

Instantiation types

+ +

+multi_index_container is instantiated with the following types: +

    +
  1. Value is the + Assignable + type of the elements contained.
  2. +
  3. IndexSpecifierList specifies the indices that the + multi_index_container is composed of. It must be a non-empty + + MPL Forward Sequence of index specifiers. For + syntactic convenience, the + indexed_by + MPL sequence can be used. +
  4. Allocator must comply with the C++ requirements for + allocators [lib.allocator.requirements]. +
+Indices of a given multi_index_container instantiation cannot have +duplicate tags, either within a single +index or in two different indices. +

+ +

Nested types

+ +ctor_args_list + +
+Although the exact definition of ctor_args_list is +implementation defined, from the user point of view this type can be +treated as equivalent to +::boost::tuple<C0,...,CI-1>, +where Ci is the ctor_args type of the +i-th index held by the multi_index_container, in the +same order as they were specified. Strictly speaking, there is an +implicit conversion from +const ::boost::tuple<C0,...,CI-1>& +to const ctor_args_list&. This type is used for +providing the construction arguments of the indices of the +multi_index_container. ctor_args_list is +Default +Constructible, provided that all ctor_args types +involved are default constructible. +
+ +index_specifier_type_list + +
+ +MPL Forward Sequence containing the types of the index specifiers +used in the instantiation of the multi_index_container, in the same order as +they were provided. +
+ +index_type_list + +
+ +MPL Forward Sequence containing the types of the indices held by +the multi_index_container, in the same order as they were specified. +
+ +iterator_type_list + +
+ +MPL Forward Sequence containing the types of the iterators of +the indices held by the multi_index_container, in the same order as they were +specified. +
+ +const_iterator_type_list + +
+ +MPL Forward Sequence containing the types of the constant +iterators of the indices held by the multi_index_container, in the same order +as they were specified. +
+ +

Nested class templates

+ +template<int N> struct nth_index + +
+nth_index<N>::type yields the type of the +N-th (0-based) index held by the multi_index_container, in +the same order as they were specified.
+Requires: 0 <= N < I. +
+ +template<typename Tag> struct index + +
+index<Tag>::type yields the type of the index which +has Tag as an associated tag type.
+Requires: Some index of the multi_index_container has Tag +as an associated tag type. +
+ +template<int N> struct nth_index_iterator + +
+nth_index_iterator<N>::type is equivalent to +nth_index<N>::type::iterator.
+
+ +template<int N> struct nth_index_const_iterator + +
+nth_index_const_iterator<N>::type is equivalent to +nth_index<N>::type::const_iterator.
+
+ +template<typename Tag> struct index_iterator + +
+index_iterator<Tag>::type is equivalent to +index<Tag>::type::iterator.
+
+ +template<typename Tag> struct index_const_iterator + +
+index_const_iterator<Tag>::type is equivalent to +index<Tag>::type::const_iterator.
+
+ +

Constructors, copy and assignment

+ +explicit multi_index_container(
+  const ctor_args_list& comp=ctor_args_list(),
+  const allocator_type& al=allocator_type());
+ +
+Effects: Constructs an empty multi_index_container using the +specified argument list and allocator.
+Complexity: Constant. +
+ +template<typename InputIterator>
+multi_index_container(
+  InputIterator first,InputIterator last,
+  const ctor_args_list& comp=ctor_args_list(),
+  const allocator_type& al=allocator_type());
+ +
+Requires: InputIterator is a model of + +Input Iterator over elements of type +Value or a type convertible to Value. +last is reachable from first.
+Effects: Constructs and empty multi_index_container using the +specified argument list and allocator and fills it with +the elements in the range [first,last). +Insertion of each element may or may not succeed depending +on the acceptance by all the indices of the multi_index_container.
+Complexity: O(m*H(m)), where m is +the number of elements in [first,last).
+
+ +multi_index_container(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& x);
+ +
+Effects: Constructs a copy of x, copying its +elements as well as its internal objects (key extractors, comparison objects, +allocator.)
+Postconditions: *this==x. The order on every index +of the multi_index_container is preserved as well.
+Complexity: O(x.size()*log(x.size()) + C(x.size())). +
+ +~multi_index_container() +
+Effects: Destroys the multi_index_container and all the elements +contained. The order in which the elements are destroyed is not specified.
+Complexity: O(n*D(n)). +
+ +multi_index_container<Value,IndexSpecifierList,Allocator>& operator=(
+  const multi_index_container<Value,IndexSpecifierList,Allocator>& x);
+ +
+Replaces the elements and internal objects of the multi_index_container +with copies from x.
+Postconditions: *this==x. The order on every index +of the multi_index_container is preserved as well.
+Returns: *this.
+Complexity: O(n*D(n) + x.size()*log(x.size()) + +C(x.size())).
+Exception safety: Strong, provided the copy and assignment operations +of the types of ctor_args_list do not throw. +
+ +allocator_type get_allocator()const; + +
+Returns a copy of the allocator_type object used to construct +the multi_index_container.
+Complexity: Constant. +
+ +

Index retrieval operations

+ +template<int N> typename nth_index<N>::type& get(); + +
+Requires: 0 <= N < I.
+Effects: Returns a reference to the +nth_index<N>::type index held by *this.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<int N> const typename nth_index<N>::type& get()const; + +
+Requires: 0 <= N < I.
+Effects: Returns a const reference to the +nth_index<N>::type index held by *this.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<typename Tag> typename index<Tag>::type& get() + +
+Requires: Tag is such that index<Tag>::type +is valid.
+Effects: Returns a reference to the +index<Tag>::type index held by +*this.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<typename Tag> const typename index<Tag>::type& get()const; + +
+Requires: Tag is such that index<Tag>::type +is valid.
+Effects: Returns a const reference to the +index<Tag>::type index held by +*this.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +

Projection operations

+ +

+Given a multi_index_container with indices i1 +and i2, we say than an i1-iterator +it1 and an i2-iterator it2 +are equivalent if: +

    +
  • it1==i1.end() AND it2==i2.end(),
  • +
  • OR it1 and it2 point to the + same element.
  • +
+

+ +template<int N,typename IteratorType>
+typename nth_index_iterator<N>::type project(IteratorType it);
+ +
+Requires: 0 <= N < I. IteratorType +belongs to iterator_type_list. it is a valid +iterator of some index of *this (i.e. does not refer to some +other multi_index_container.)
+Effects: Returns an nth_index_iterator<N>::type iterator +equivalent to it.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<int N,typename IteratorType>
+typename nth_index_const_iterator<N>::type project(IteratorType it)const;
+ +
+Requires: 0 <= N < I. IteratorType +belongs to const_iterator_type_list or +iterator_type_list. it is a +valid (constant or non-constant) iterator of some index of *this +(i.e. does not refer to some other multi_index_container.)
+Effects: Returns an nth_index_const_iterator<N>::type +iterator equivalent to it.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<typename Tag,typename IteratorType>
+typename index_iterator<Tag>::type project(IteratorType it);
+ +
+Requires: Tag is such that +index_iterator<Tag>::type is valid. IteratorType +belongs to iterator_type_list. it is a valid +iterator of some index of *this (i.e. does not refer to some +other multi_index_container.)
+Effects: Returns an index_iterator<Tag>::type iterator +equivalent to it.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +template<typename Tag,typename IteratorType>
+typename index_const_iterator<Tag>::type project(IteratorType it)const;
+ +
+Requires: Tag is such that +index_const_iterator<Tag>::type is valid. IteratorType +belongs to const_iterator_type_list or +iterator_type_list. it is a valid +(constant or non-constant) iterator of some index of *this +(i.e. does not refer to some other multi_index_container.)
+Effects: Returns an index_const_iterator<Tag>::type +iterator equivalent to it.
+Complexity: Constant.
+Exception safety: nothrow. +
+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/reference/ord_indices.html b/doc/reference/ord_indices.html new file mode 100644 index 0000000..a64b64a --- /dev/null +++ b/doc/reference/ord_indices.html @@ -0,0 +1,909 @@ + + + + + +Boost.MultiIndex Documentation - Ordered indices reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Ordered indices reference

+ + + +
+ +
+ +

Contents

+ + + +

+Header + +"boost/multi_index/ordered_index_fwd.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+// index specifiers unique and ordered_non_unique
+
+template<consult ordered_unique reference for arguments>
+struct ordered_unique;
+template<consult ordered_non_unique reference for arguments>
+struct ordered_non_unique;
+
+// indices
+
+namespace detail{
+
+template<implementation defined> class index name is implementation defined;
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

+index_fwd.hpp provides forward declarations for index specifiers +ordered_unique and ordered_non_unique and +their associated ordered index classes. +

+ +

+Header + +"boost/multi_index/ordered_index.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+// index specifiers unique and ordered_non_unique
+
+template<consult ordered_unique reference for arguments>
+struct ordered_unique;
+template<consult ordered_non_unique reference for arguments>
+struct ordered_non_unique;
+
+// indices
+
+namespace detail{
+
+template<implementation defined> class index class name implementation defined;
+
+// index comparison:
+
+// OP is any of ==,<,!=,>,>=,<=
+
+template<arg set 1,arg set 2>
+bool operator OP(
+  const index class name<arg set 1>& x,const index class name<arg set 2>& y);
+
+// index specialized algorithms:
+
+template<implementation defined>
+void swap(index class name& x,index class name& y);
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

+Index specifiers ordered_unique and ordered_non_unique +

+ +

+These index specifiers allow +for insertion of ordered indices without and with +allowance of duplicate elements, respectively. The syntax of ordered_unique +and ordered_non_unique coincide, thus we describe them in a grouped manner. +ordered_unique and ordered_non_unique can be instantiated in +two different forms, according to whether a tag list for the index is provided or not: +

+ +
+template<
+  typename KeyFromValue,
+  typename Compare=std::less<KeyFromValue::result_type
+>
+struct (ordered_unique | ordered_non_unique);
+
+template<
+  typename TagList,
+  typename KeyFromValue,
+  typename Compare=std::less<KeyFromValue::result_type>
+>
+struct (ordered_unique | ordered_non_unique);
+
+ +

+If provided, TagList must be an instantiation of the class template +tag. +The template arguments are used by the corresponding index implementation, +refer to the ordered indices reference section for further +explanations on their acceptable type values. +

+ +

Ordered indices

+ +

+An ordered index provides a set-like interface to the underlying heap of +elements contained in a multi_index_container. An ordered index is +particularized according to a given +Key Extractor +that retrieves keys from elements of multi_index_container and a comparison +predicate. +

+ +

+There are two variants of ordered indices: unique, which do +not allow duplicate elements (with respect to its associated comparison +predicate) and non-unique, which accept those duplicates. +The interface of these two variants is the same, so they are documented +together, with minor differences explicitly stated when they exist. +

+ +

+Except where noted, ordered indices (both unique and non-unique) are models of + +Sorted Associative Container and + +Unique Associative Container, much as std::sets +are. Accordingly, validity of iterators and references to elements is +preserved. We only provide descriptions of those types and operations that are +either not present in the concepts modeled or do not exactly conform to the +requirements for these types of containers. +

+ +
+namespace boost{
+
+namespace multi_index{
+
+namespace{ implementation defined unbounded; } // see range()
+
+namespace detail{
+
+template<implementation defined: dependent on types Value, Allocator,
+  TagList, KeyFromValue, Compare>
+class name is implementation defined
+{ 
+public:
+  // types:
+
+  typedef typename KeyFromValue::result_type         key_type;
+  typedef Value                                      value_type;
+  typedef KeyFromValue                               key_from_value;
+  typedef Compare                                    key_compare;
+  typedef implementation defined                     value_compare;
+  typedef tuple<key_from_value,key_compare>          ctor_args;
+  typedef Allocator                                  allocator_type;
+  typedef typename Allocator::reference              reference;
+  typedef typename Allocator::const_reference        const_reference;
+  typedef implementation defined                     iterator;
+  typedef implementation defined                     const_iterator;
+  typedef implementation defined                     size_type;      
+  typedef implementation defined                     difference_type;
+  typedef typename Allocator::pointer                pointer;
+  typedef typename Allocator::const_pointer          const_pointer;
+  typedef equivalent to
+    std::reverse_iterator<iterator>                  reverse_iterator;
+  typedef equivalent to
+    std::reverse_iterator<const_iterator>            const_reverse_iterator;
+
+  // construct/copy/destroy:
+
+  index class name& operator=(const index class name& x);
+
+  allocator_type get_allocator()const;
+
+  // iterators:
+
+  iterator               begin();
+  const_iterator         begin()const;
+  iterator               end();
+  const_iterator         end()const;
+  reverse_iterator       rbegin();
+  const_reverse_iterator rbegin()const;
+  reverse_iterator       rend();
+  const_reverse_iterator rend()const;
+ 
+  // capacity:
+
+  bool      empty()const;
+  size_type size()const;
+  size_type max_size()const;
+
+  // modifiers:
+
+  std::pair<iterator,bool> insert(const value_type& x);
+  iterator insert(iterator position,const value_type& x);
+  template<typename InputIterator>
+  void insert(InputIterator first,InputIterator last);
+
+  void erase(iterator position);
+  size_type erase(const key_type& x);
+  void erase(iterator first,iterator last);
+
+  bool replace(iterator position,const value_type& x);
+  template<typename Modifier> bool modify(iterator position,Modifier mod);
+  template<typename Modifier> bool modify_key(iterator position,Modifier mod);
+  
+  void swap(index class name& x);
+  void clear();
+
+  // observers:
+
+  key_from_value key_extractor()const;
+  key_compare    key_comp()const;
+  value_compare  value_comp()const;
+
+  // set operations:
+
+  template<typename CompatibleKey>
+  const_iterator find(const CompatibleKey& x)const;
+  template<typename CompatibleKey,typename CompatibleCompare>
+  const_iterator find(
+    const CompatibleKey& x,const CompatibleCompare& comp)const;
+
+  template<typename CompatibleKey>
+  size_type count(const CompatibleKey& x)const;
+  template<typename CompatibleKey,typename CompatibleCompare>
+  size_type count(const CompatibleKey& x,const CompatibleCompare& comp)const;
+
+  template<typename CompatibleKey>
+  const_iterator lower_bound(const CompatibleKey& x)const;
+  template<typename CompatibleKey,typename CompatibleCompare>
+  const_iterator lower_bound(
+    const CompatibleKey& x,const CompatibleCompare& comp)const;
+
+  template<typename CompatibleKey>
+  const_iterator upper_bound(const CompatibleKey& x)const;
+  template<typename CompatibleKey,typename CompatibleCompare>
+  const_iterator upper_bound(
+    const CompatibleKey& x,const CompatibleCompare& comp)const;
+
+  template<typename CompatibleKey>
+  std::pair<const_iterator,const_iterator> equal_range(
+    const CompatibleKey& x)const;
+  template<typename CompatibleKey,typename CompatibleCompare>
+  std::pair<const_iterator,const_iterator> equal_range(
+    const CompatibleKey& x,const CompatibleCompare& comp)const;
+
+  // range:
+
+  template<typename LowerBounder,typename UpperBounder>
+  std::pair<const_iterator,const_iterator> range(
+    LowerBounder lower,UpperBounder upper)const;
+};
+
+// index comparison:
+
+template<arg set 1,arg set 2>
+bool operator==(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin());
+}
+
+template<arg set 1,arg set 2>
+bool operator<(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
+}
+
+template<arg set 1,arg set 2>
+bool operator!=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x==y);
+}
+
+template<arg set 1,arg set 2>
+bool operator>(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return y<x;
+}
+
+template<arg set 1,arg set 2>
+bool operator>=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x<y);
+}
+
+template<arg set 1,arg set 2>
+bool operator<=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x>y);
+}
+
+// index specialized algorithms:
+
+template<implementation defined>
+void swap(index class name& x,index class name& y);
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Complexity signature

+ +

+Here and in the descriptions of operations of ordered indices, we adopt the +scheme outlined in the +complexity signature +section. The complexity signature of ordered indices is: +

    +
  • copying: c(n)=n*log(n),
  • +
  • insertion: i(n)=log(n),
  • +
  • hinted insertion: h(n)=1 (constant) if the hint element + precedes the point of insertion, h(n)=log(n) otherwise,
  • +
  • deletion: d(n)=1 (constant),
  • +
  • replacement: r(n)=1 (constant) if the element position does not + change, r(n)=log(n) otherwise,
  • +
  • modifying: m(n)=1 (constant) if the element position does not + change, m(n)=log(n) otherwise.
  • +
+

+ +

Instantiation types

+ +

Ordered indices are instantiated internally to multi_index_container and +specified by means of indexed_by +with index specifiers ordered_unique +and ordered_non_unique. Instantiations are dependent on the +following types: +

    +
  • Value from multi_index_container,
  • +
  • Allocator from multi_index_container,
  • +
  • TagList from the index specifier (if provided),
  • +
  • KeyFromValue from the index specifier,
  • +
  • Compare from the index specifier.
  • +
+TagList must be an instantiation of +tag. The type KeyFromValue, +which determines the mechanism for extracting a key from Value, +must be a model of +Key Extractor from Value. Compare is a + +Strict Weak Ordering on elements of +KeyFromValue::result_type. +

+ +

Constructors, copy and assignment

+ +

+As explained in the index +concepts section, indices do not have public constructors or destructors. +Assignment, on the other hand, is provided. +

+ +index class name& operator=(const index class name& x); + +
+Effects: +
+a=b;
+
+where a and b are the multi_index_container +objects to which *this and x belongs, respectively.
+Returns: *this.
+
+ +

Modifiers

+ +std::pair<iterator,bool> insert(const value_type& x); + +
+Effects: Inserts x into the multi_index_container to which +the index belongs if +
    +
  • the index is non-unique OR no other element exists with + equivalent key,
  • +
  • AND insertion is allowed by all other indices of the + multi_index_container.
  • +
+Returns: The return value is a pair p. p.second +is true if and only if insertion took place. On successful insertion, +p.first points to the element inserted; otherwise, p.first +points to an element that caused the insertion to be banned. Note that more than +one element can be causing insertion not to be allowed.
+Complexity: O(I(n)).
+Exception safety: Strong.
+
+ +iterator insert(iterator position,const value_type& x); + +
+Requires: position is a valid iterator of the index.
+Effects: Inserts x into the multi_index_container to which +the index belongs if +
    +
  • the index is non-unique OR no other element exists with + equivalent key,
  • +
  • AND insertion is allowed by all other indices of the + multi_index_container.
  • +
+position is used as a hint to improve the efficiency of the +operation.
+Returns: On successful insertion, an iterator to the newly inserted +element. Otherwise, an iterator to an element that caused the insertion to be +banned. Note that more than one element can be causing insertion not to be +allowed.
+Complexity: O(H(n)).
+Exception safety: Strong.
+
+ +template<typename InputIterator>
+void insert(InputIterator first,InputIterator last);
+ +
+Requires: InputIterator is a model of + +Input Iterator over elements of type +value_type or a type convertible to value_type. +first and last are not iterators into any +index of the multi_index_container to which this index belongs. +last is reachable from first.
+Effects: +
+iterator hint=end();
+while(first!=last)hint=insert(hint,*first++);
+
+Complexity: O(m*H(n+m)), where +m is the number of elements in [first, +last).
+Exception safety: Basic.
+
+ +void erase(iterator position); + +
+Requires: position is a valid dereferenceable iterator +of the index.
+Effects: Deletes the element pointed to by position.
+Complexity: O(D(n)).
+Exception safety: nothrow.
+
+ +size_type erase(const key_type& x); + +
+Effects: Deletes the elements with key equivalent to x.
+Returns: Number of elements deleted.
+Complexity: O(log(n) + m*D(n)), where m is +the number of elements deleted.
+Exception safety: nothrow.
+
+ +void erase(iterator first,iterator last); + +
+Requires: [first,last) is a valid +range of the index.
+Effects: Deletes the elements in [first,last).
+Complexity: O(log(n) + m*D(n)), where m is +the number of elements in [first,last).
+Exception safety: nothrow.
+
+ +bool replace(iterator position,const value_type& x); + +
+Requires: position is a valid dereferenceable iterator +of the index.
+Effects: Assigns the value x to the element pointed +to by position into the multi_index_container to which +the index belongs if, for the value x +
    +
  • the index is non-unique OR no other element exists + (except possibly *position) with equivalent key,
  • +
  • AND replacing is allowed by all other indices of the + multi_index_container.
  • +
+Postconditions: Validity of position is preserved +in all cases.
+Returns: true if the replacement took place, +false otherwise.
+Complexity: O(R(n)).
+Exception safety: Strong. If an exception is thrown by some +user-provided operation the multi_index_container to which the index +belongs remains in its original state. +
+ + +template<typename Modifier> bool modify(iterator position,Modifier mod); + +
+Requires: Modifier is a model of + +Unary Function accepting arguments of type +value_type&. position is a valid dereferenceable +iterator of the index.
+Effects: Calls mod(e) where e is the element +pointed to by position and rearranges *position into +all the indices of the multi_index_container. Rearrangement is successful if +
    +
  • the index is non-unique OR no other element exists + with equivalent key,
  • +
  • AND rearrangement is allowed by all other indices of the + multi_index_container.
  • +
+If the rearrangement fails, the element is erased.
+Postconditions: Validity of position is preserved if the +operation succeeds.
+Returns: true if the operation succeeded, false +otherwise.
+Complexity: O(M(n)).
+Exception safety: Basic. If an exception is thrown by some +user-provided operation (except possibly mod), then +the element pointed to by position is erased. +
+ + +template<typename Modifier> bool modify_key(iterator position,Modifier mod); + +
+Requires: key_from_value is a read/write +Key Extractor +from value_type. Modifier is a model of + +Unary Function accepting arguments of type +key_type&. position is a valid dereferenceable +iterator of the index.
+Effects: Calls mod(k) where k is the key +obtained by the internal KeyFromValue object of the index from +the element pointed to by position, and rearranges +*position into all the indices of the multi_index_container. +Rearrangement is successful if +
    +
  • the index is non-unique OR no other element exists + with equivalent key,
  • +
  • AND rearrangement is allowed by all other indices of the + multi_index_container.
  • +
+If the rearrangement fails, the element is erased.
+Postconditions:Validity of position is preserved if +the operation succeeds.
+Returns: true if the operation succeeded, false +otherwise.
+Complexity: O(M(n)).
+Exception safety: Basic. If an exception is thrown by some +user-provided operation (except possibly mod), then +the element pointed to by position is erased. +
+ +

Observers

+ +

Apart from standard key_comp and value_comp, +ordered indices have a member function for retrieving the internal key extractor +used. +

+ +key_from_value key_extractor()const; + +
+Returns a copy of the key_from_value object used to construct +the index.
+Complexity: Constant. +
+ +

Set operations

+ +

+Ordered indices provide the full lookup functionality required by + +Sorted Associative Containers and + +Unique Associative Containers, namely find, +count, lower_bound, upper_bound +and equal_range. Additionally, these member functions are +templatized to allow for non-standard arguments, so extending +the types of search operations allowed. The kind of arguments permissible +when invoking the lookup member functions is defined by the following +concept. +

+ +

+Consider a + +Strict Weak Ordering Compare over values +of type Key. A pair of types (CompatibleKey, +CompatibleCompare) is said to be a compatible extension +of Compare if +

    +
  1. CompatibleCompare is a + + Binary Predicate over (Key, + CompatibleKey),
  2. +
  3. CompatibleCompare is a + + Binary Predicate over (CompatibleKey, + Key),
  4. +
  5. if c_comp(ck,k1) then !c_comp(k1,ck),
  6. +
  7. if !c_comp(ck,k1) and !comp(k1,k2) then + !c_comp(ck,k2),
  8. +
  9. if !c_comp(k1,ck) and !comp(k2,k1) then + !c_comp(k2,ck),
  10. +
+for every c_comp of type CompatibleCompare, +comp of type Compare, ck of type +CompatibleKey and k1, k2 of type +Key. +

+ + + +

Additionally, a type CompatibleKey is said to be a +compatible key of Compare if (CompatibleKey, +Compare) is a compatible extension of Compare. +This implies that Compare, as well as being a strict +weak ordering, accepts arguments of type CompatibleKey, +which usually means it has several overloads of operator(). +

+ +

+In the context of a compatible extension or a compatible key, the expressions +"equivalent", "less than" and "greater than" take on their obvious +interpretations. +

+ +template<typename CompatibleKey> const_iterator find(const CompatibleKey& x)const; + + +
+Requires: CompatibleKey is a compatible key of +key_compare.
+Effects: Returns a pointer to an element whose key is equivalent to +x, or end() if such an element does not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey,typename CompatibleCompare>
+const_iterator find(const CompatibleKey& x,const CompatibleCompare& comp)const; +
+ +
+Requires: (CompatibleKey, CompatibleCompare) +is a compatible extension of key_compare.
+Effects: Returns a pointer to an element whose key is equivalent to +x, or end() if such an element does not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey> size_type
+count(const CompatibleKey& x)const; +
+ +
+Requires: CompatibleKey is a compatible key of +key_compare.
+Effects: Returns the number of elements with key equivalent to x.
+Complexity: O(log(n) + count(x)).
+
+ +template<typename CompatibleKey,typename CompatibleCompare>
+size_type count(const CompatibleKey& x,const CompatibleCompare& comp)const; +
+ +
+Requires: (CompatibleKey, CompatibleCompare) +is a compatible extension of key_compare.
+Effects: Returns the number of elements with key equivalent to x.
+Complexity: O(log(n) + count(x,comp)).
+
+ +template<typename CompatibleKey>
+const_iterator lower_bound(const CompatibleKey& x)const; +
+ +
+Requires: CompatibleKey is a compatible key of +key_compare.
+Effects: Returns an iterator pointing to the first element with +key not less than x, or end() if such an element does +not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey,typename CompatibleCompare>
+const_iterator lower_bound(const CompatibleKey& x,const CompatibleCompare& comp)const; +
+ +
+Requires: (CompatibleKey, CompatibleCompare) +is a compatible extension of key_compare.
+Effects: Returns an iterator pointing to the first element with +key not less than x, or end() if such an element does +not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey>
+const_iterator upper_bound(const CompatibleKey& x)const; +
+ +
+Requires: CompatibleKey is a compatible key of +key_compare.
+Effects: Returns an iterator pointing to the first element with +key greater than x, or end() if such an element does +not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey,typename CompatibleCompare>
+const_iterator upper_bound(const CompatibleKey& x,const CompatibleCompare& comp)const; +
+ +
+Requires: (CompatibleKey, CompatibleCompare) +is a compatible extension of key_compare.
+Effects: Returns an iterator pointing to the first element with +key greater than x, or end() if such an element does +not exist.
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey>
+std::pair<const_iterator,const_iterator> equal_range(
+  const CompatibleKey& x)const; +
+ +
+Requires: CompatibleKey is a compatible key of +key_compare.
+Effects: Equivalent to make_pair(lower_bound(x),upper_bound(x)).
+Complexity: O(log(n)).
+
+ +template<typename CompatibleKey,typename CompatibleCompare>
+std::pair<const_iterator,const_iterator> equal_range(
+  const CompatibleKey& x,const CompatibleCompare& comp)const; +
+ +
+Requires: (CompatibleKey, CompatibleCompare) +is a compatible extension of key_compare.
+Effects: Equivalent to +make_pair(lower_bound(x,comp),upper_bound(x,comp)).
+Complexity: O(log(n)).
+
+ + +

Range operations

+ +

+The member function range is not defined for sorted associative +containers, but ordered indices provide it as a convenient utility. A range +or interval is defined by two conditions for the lower and upper bounds, which +are modeled after the following concepts. +

+ +

+Consider a + +Strict Weak Ordering Compare over values +of type Key. A type LowerBounder is said to be +a lower bounder of Compare if +

    +
  1. LowerBounder is a + + Predicate over Key,
  2. +
  3. if lower(k1) and !comp(k2,k1) then + lower(k2),
  4. +
+for every lower of type LowerBounder, +comp of type Compare, and k1, +k2 of type Key. Similarly, an upper bounder +is a type UpperBounder such that +
    +
  1. UpperBounder is a + + Predicate over Key,
  2. +
  3. if upper(k1) and !comp(k1,k2) then + upper(k2),
  4. +
+for every upper of type UpperBounder, +comp of type Compare, and k1, +k2 of type Key. +

+ +template<typename LowerBounder,typename UpperBounder>
+std::pair<const_iterator,const_iterator> range(
+  LowerBounder lower,UpperBounder upper)const; +
+ +
+Requires: LowerBounder and UpperBounder are +a lower and upper bounder of key_compare, respectively.
+Effects: Returns a pair of iterators pointing to the beginning and one +past the end of the subsequence of elements satisfying lower and +upper simultaneously. If no such elements exist, the iterators +both point to the first element satisfying lower, or else +are equal to end() if this latter element does not exist.
+Complexity: O(log(n)).
+Variants: In place of lower or upper (or both), +the singular value boost::multi_index::unbounded can be +provided. This acts as a predicate which all values of type key_type +satisfy.
+
+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/reference/seq_indices.html b/doc/reference/seq_indices.html new file mode 100644 index 0000000..6df02e6 --- /dev/null +++ b/doc/reference/seq_indices.html @@ -0,0 +1,837 @@ + + + + + +Boost.MultiIndex Documentation - Sequenced indices reference + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Sequenced indices reference

+ + + +
+ +
+ +

Contents

+ + + +

+Header + +"boost/multi_index/sequenced_index_fwd.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+// sequenced index specifier
+
+template<typename TagList=tag<> > struct sequenced;
+
+// indices
+
+namespace detail{
+
+template<implementation defined> class index class name implementation defined;
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

+sequenced_index_fwd.hpp provides forward declarations for the +sequenced index specifier and +its associated sequenced index class. +

+ +

+Header + +"boost/multi_index/sequenced_index.hpp" synopsis

+ +
+namespace boost{
+
+namespace multi_index{
+
+// sequenced index specifier
+
+template<typename TagList=tag<> > struct sequenced;
+
+// indices
+
+namespace detail{
+
+template<implementation defined> class index class name implementation defined;
+
+// index comparison:
+
+// OP is any of ==,<,!=,>,>=,<=
+
+template<arg set 1,arg set 2>
+bool operator OP(
+  const index class name<arg set 1>& x,const index class name<arg set 2>& y);
+
+// index specialized algorithms:
+
+template<implementation defined>
+void swap(index class name& x,index class name& y);
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

+sequenced index specifier +

+ +

+This index specifier allows for insertion of a sequenced +index.

+ +
+template<typename TagList=tag<> > struct sequenced;
+
+ +

If provided, TagList must be an instantiation of +tag. +

+ +

Sequenced indices

+ +

+Sequenced indices are modeled after the semantics of a bidirectional list +like std::list. Elements in a sequenced index are by default +sorted according to their order of insertion: this means that new elements +inserted through a different index of the multi_index_container are appended +to the end of the sequenced index. Additionally, the index allows for free +reordering of elements in the same vein as std::list does. +

+ +

+There are a number of differences with respect to std::lists: +

    +
  • Sequenced indices are not + Assignable (like any other index.)
  • +
  • Unlike as in std::list, insertions into a sequenced index + may fail due to clashings with other indices. This alters the semantics + of the operations provided with respect to their analogues in + std::list. +
  • +
  • Elements in a sequenced index are not mutable, and can only be changed + by means of replace and + modify member functions. +
  • +
+Having these restrictions into account, sequenced indices are models +of +Reversible Container, + +Front Insertion Sequence and + +Back Insertion Sequence. We only provide descriptions +of those types and operations that are that are either not present in the +concepts modeled or do not exactly conform to the requirements for these +types of containers. +

+ +
+namespace boost{
+
+namespace multi_index{
+
+namespace detail{
+
+template<implementation defined: dependent on types Value, Allocator, TagList>
+class name is implementation defined
+{ 
+public:
+  // types:
+
+  typedef typename node_type::value_type             value_type;
+  typedef tuples::null_type                          ctor_args;
+  typedef typename Allocator                         allocator_type;
+  typedef typename allocator_type::reference         reference;
+  typedef typename allocator_type::const_reference   const_reference;
+  typedef implementation defined                     iterator;
+  typedef implementation defined                     const_iterator;
+  typedef std::size_t                                size_type;      
+  typedef std::ptrdiff_t                             difference_type;
+  typedef typename allocator_type::pointer           pointer;
+  typedef typename allocator_type::const_pointer     const_pointer;
+  typedef equivalent to
+    std::reverse_iterator<iterator>                  reverse_iterator;
+  typedef equivalent to
+    std::reverse_iterator<const_iterator>            const_reverse_iterator;
+
+  // construct/copy/destroy:
+
+  index class name& operator=(const index class name& x);
+
+  template <class InputIterator>
+  void assign(InputIterator first,InputIterator last);
+  void assign(size_type n,const value_type& value);
+    
+  allocator_type get_allocator()const;
+
+  // iterators:
+
+  iterator               begin();
+  const_iterator         begin()const;
+  iterator               end();
+  const_iterator         end()const;
+  reverse_iterator       rbegin();
+  const_reverse_iterator rbegin()const;
+  reverse_iterator       rend();
+  const_reverse_iterator rend()const;
+
+  // capacity:
+
+  bool      empty()const;
+  size_type size()const;
+  size_type max_size()const;
+
+  void resize(size_type n,const value_type& x=value_type());
+
+  // access:
+
+  const_reference front()const;
+  const_reference back()const;
+
+  // modifiers:
+
+  std::pair<iterator,bool> push_front(const value_type& x);
+  void                     pop_front();
+  std::pair<iterator,bool> push_back(const value_type& x);
+  void                     pop_back();
+
+  std::pair<iterator,bool> insert(iterator position,const value_type& x);
+  void insert(iterator position,size_type n,const value_type& x);
+  template<typename InputIterator>
+  void insert(iterator position,InputIterator first,InputIterator last);
+
+  void erase(iterator position);
+  void erase(iterator first,iterator last);
+
+  bool replace(iterator position,const value_type& x);
+  template<typename Modifier> bool modify(iterator position,Modifier mod);
+
+  void swap(index class name& x);
+
+  void clear();
+
+  // list operations:
+
+  void splice(iterator position,index class name& x);
+  void splice(iterator position,index class name& x,iterator i);
+  void splice(
+    iterator position,index class name& x,iterator first,iterator last);
+
+  void remove(const value_type& value);
+  template<typename Predicate> void remove_if(Predicate pred);
+
+  void ordered_unique();
+  template <class BinaryPredicate>
+  void ordered_unique(BinaryPredicate binary_pred);
+
+  void merge(index class name& x);
+  template <typename Compare> void merge(index class name& x,Compare comp);
+
+  void sort();
+  template <typename Compare> void sort(Compare comp);
+
+  void reverse();
+
+  // relocate operations:
+
+  void relocate(iterator position,iterator i); 
+  void relocate(iterator position,iterator first,iterator last);
+}
+
+// index comparison:
+
+template<arg set 1,arg set 2>
+bool operator==(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin());
+}
+
+template<arg set 1,arg set 2>
+bool operator<(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end());
+}
+
+template<arg set 1,arg set 2>
+bool operator!=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x==y);
+}
+
+template<arg set 1,arg set 2>
+bool operator>(
+  const index class name<arg set 1>& x
+  ,const index class name<arg set 2>& y)
+{
+  return y<x;
+}
+
+template<arg set 1,arg set 2>
+bool operator>=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x<y);
+}
+
+template<arg set 1,arg set 2>
+bool operator<=(
+  const index class name<arg set 1>& x,
+  const index class name<arg set 2>& y)
+{
+  return !(x>y);
+}
+
+// index specialized algorithms:
+
+template<implementation defined>
+void swap(index class name& x,index class name& y);
+
+} // namespace boost::multi_index::detail
+
+} // namespace boost::multi_index 
+
+} // namespace boost
+
+ +

Complexity signature

+ +

+Here and in the descriptions of operations of sequenced indices, we adopt the +scheme outlined in the +complexity signature +section. The complexity signature of sequenced indices is: +

    +
  • copying: c(n)=n*log(n),
  • +
  • insertion: i(n)=1 (constant),
  • +
  • hinted insertion: h(n)=1 (constant),
  • +
  • deletion: d(n)=1 (constant),
  • +
  • replacement: r(n)=1 (constant),
  • +
  • modifying: m(n)=1 (constant).
  • +
+

+ +

Instantiation types

+ +

Sequenced indices are instantiated internally to multi_index_container +and specified by means of +indexed_by with the sequenced +index specifier. Instantiations are dependent on the following types: +

    +
  • Value from multi_index_container,
  • +
  • Allocator from multi_index_container,
  • +
  • TagList from the index specifier (if provided).
  • +
+TagList must be an instantiation of +tag. +

+ +

Constructors, copy and assignment

+ +

+As explained in the index +concepts section, indices do not have public constructors or destructors. +Assignment, on the other hand, is provided. +

+ +index class name& operator=(const index class name& x); + +
+Effects: +
+a=b;
+
+where a and b are the multi_index_container +objects to which *this and x belongs, respectively.
+Returns: *this.
+
+ +template <class InputIterator>
+void assign(InputIterator first,InputIterator last);
+ +
+Requires: InputIterator is a model of + +Input Iterator over elements of type +value_type or a type convertible to value_type. +first and last are not iterators into any +index of the multi_index_container to which this index belongs. +last is reachable from first.
+Effects: +
+clear();
+insert(end(),first,last);
+
+
+ +void assign(size_type n,const value_type& value); + +
+Effects: +
+clear();
+for(size_type i=0;i<n;++n)push_back(v);
+
+
+ +

Capacity operations

+ +void resize(size_type n,const value_type& x=value_type()); + +
+Effects: +
+if(n>size())insert(end(),n-size(),x);
+else if(n<size())erase(begin()+n,end());
+
+Note: If an expansion is requested, the size of the index is not guaranteed +to be n after this operation (other indices may ban insertions.) +
+ +

Modifiers

+ +std::pair<iterator,bool> push_front(const value_type& x); + +
+Effects: Inserts x at the beginning of the sequence if +no other index of the multi_index_container bans the insertion.
+Returns: The return value is a pair p. p.second +is true if and only if insertion took place. On successful +insertion, p.first points to the element inserted; otherwise, +p.first points to an element that caused the insertion to be banned. +Note that more than one element can be causing insertion not to be allowed.
+Complexity: O(I(n)).
+Exception safety: Strong. +
+ +std::pair<iterator,bool> push_back(const value_type& x); + +
+Effects: Inserts x at the end of the sequence if +no other index of the multi_index_container bans the insertion.
+Returns: The return value is a pair p. p.second +is true if and only if insertion took place. On successful +insertion, p.first points to the element inserted; otherwise, +p.first points to an element that caused the insertion to be banned. +Note that more than one element can be causing insertion not to be allowed.
+Complexity: O(I(n)).
+Exception safety: Strong. +
+ +iterator insert(iterator position,const value_type& x); + +
+Requires: position is a valid iterator of the index.
+Effects: Inserts x before position if insertion +is allowed by all other indices of the multi_index_container.
+Returns: On successful insertion, an iterator to the newly inserted +element. Otherwise, an iterator to an element that caused the insertion to be +banned. Note that more than one element can be causing insertion not to be +allowed.
+Complexity: O(I(n)).
+Exception safety: Strong. +
+ +void insert(iterator position,size_type n,const value_type& x); + +
+Requires: position is a valid iterator of the index.
+Effects: +
+for(size_type i=0;i<n;++i)insert(position,x);
+
+
+ +template<typename InputIterator>
+void insert(iterator position,InputIterator first,InputIterator last);
+ +
+Requires: position is a valid iterator of the index. +InputIterator is a model of + +Input Iterator over elements of type +value_type or a type convertible to value_type. +first and last are not iterators into any +index of the multi_index_container to which this index belongs. +last is reachable from first.
+Effects: +
+while(first!=last)insert(position,*first++);
+
+Complexity: O(m*I(n+m)), where m is the +number of elements in [first,last).
+Exception safety: Basic. +
+ +void erase(iterator position); + +
+Requires: position is a valid dereferenceable iterator +of the index.
+Effects: Deletes the element pointed to by position.
+Complexity: O(D(n)).
+Exception safety: nothrow.
+
+ +void erase(iterator first,iterator last); + +
+Requires: [first,last) is a valid +range of the index.
+Effects: Deletes the elements in [first,last).
+Complexity: O(m*D(n)), where m is +the number of elements in [first,last).
+Exception safety: nothrow.
+
+ +bool replace(iterator position,const value_type& x); + +
+Requires: position is a valid dereferenceable iterator +of the index.
+Effects: Assigns the value x to the element pointed +to by position into the multi_index_container to which +the index belongs if replacing is allowed by all other indices of the +multi_index_container.
+Postconditions: Validity of position is preserved +in all cases.
+Returns: true if the replacement took place, +false otherwise.
+Complexity: O(R(n)).
+Exception safety: Strong. If an exception is thrown by some +user-provided operation the multi_index_container to which the index +belongs remains in its original state. +
+ + +template<typename Modifier> bool modify(iterator position,Modifier mod); + +
+Requires: Modifier is a model of + +Unary Function accepting arguments of type +value_type&. position is a valid dereferenceable +iterator of the index.
+Effects: Calls mod(e) where e is the element +pointed to by position and rearranges *position into +all the indices of the multi_index_container. Rearrangement on sequenced +indices does not change the position of the element with respect to the index; +rearrangement on other indices may or might not suceed. If the rearrangement +fails, the element is erased.
+Postconditions: Validity of position is preserved if the +operation succeeds.
+Returns: true if the operation succeeded, false +otherwise.
+Complexity: O(M(n)).
+Exception safety: Basic. If an exception is thrown by some +user-provided operation (except possibly mod), then +the element pointed to by position is erased. +
+ +

List operations

+ +

+Sequenced indices provides the full set of list operations provided by +std::list; the semantics of these member functions, however, +differ from that of std::list in some cases as insertions +might not succeed due to banning by other indices. Similarly, the complexity +of the operations may depend on the other indices belonging to the +same multi_index_container. +

+ +void splice(iterator position,index class name& x); + +
+Requires: position is a valid iterator of the index. +&x!=this.
+Effects: Inserts the contents of x before position, +in the same order as they were in x. Those elements succesfully +inserted are erased from x.
+Complexity: O(x.size()*I(n+x.size()) + x.size()*D(x.size())).
+Exception safety: Basic.
+
+ +void splice(iterator position,index class name& x,iterator i); + +
+Requires: position is a valid iterator of the index. +i is a valid dereferenceable iterator x.
+Effects: Inserts the element pointed to by i before +position: if insertion is succesful, the element is erased from +x. In the special case &x==this, no copy or +deletion is performed, and the operation is always succesful. If +position==i, no operation is performed.
+Postconditions: If &x==this, no iterator or reference +is invalidated.
+Complexity: If &x==this, constant; otherwise +O(I(n) + D(n)).
+Exception safety: If &x==this, nothrow; +otherwise, strong.
+
+ +void splice(iterator position,index class name& x,iterator first,iterator last); + +
+Requires: position is a valid iterator of the index. +first and last are valid iterators of x. +last is reachable from first. position +is not in the range [first,last).
+Effects: For each element in the range [first,last), +insertion is tried before position; if the operation is succesful, +the element is erased from x. In the special case +&x==this, no copy or deletion is performed, and insertions are +always succesful.
+Postconditions: If &x==this, no iterator or reference +is invalidated.
+Complexity: If &x==this, constant; otherwise +O(m*I(n+m) + m*D(x.size())) where m is the number +of elements in [first,last).
+Exception safety: If &x==this, nothrow; +otherwise, basic.
+
+ +void remove(const value_type& value); + +
+Effects: Erases all elements of the index which compare equal to +value.
+Complexity: O(n + m*D(n)), where m +is the number of elements erased.
+Exception safety: Basic. +
+ +template<typename Predicate> void remove_if(Predicate pred); + +
+Effects: Erases all elements x of the index for which +pred(x) holds..
+Complexity: O(n + m*D(n)), where m +is the number of elements erased.
+Exception safety: Basic. +
+ +void unique(); + +
+Effects: Eliminates all but the first element from every consecutive +group of equal elements referred to by the iterator i in the range +[first+1,last) for which *i==*(i-1).
+Complexity: O(n + m*D(n)), where m +is the number of elements erased.
+Exception safety: Basic. +
+ +template <class BinaryPredicate> void unique(BinaryPredicate binary_pred); + +
+Effects: Eliminates all but the first element from every consecutive +group of elements referred to by the iterator i in the range +[first+1,last) for which +binary_pred(*i,*(i-1)) holds.
+Complexity: O(n + m*D(n)), where m +is the number of elements erased.
+Exception safety: Basic. +
+ +void merge(index class name& x); + +
+Requires: std::less<value_type> is a + +Strict Weak Ordering over value_type. +Both the index and x are sorted according to +std::less<value_type>.
+Effects: Attempts to insert every element of x into the +corresponding position of the index (according to the order). Elements +successfully inserted are erased from x. The resulting sequence +is stable, i.e. equivalent elements of either container preserve their +relative position. In the special case &x==this, no operation +is performed.
+Postconditions: Elements in the index and remaining elements in +x are sorted. +Validity of iterators to the index and of non-erased elements of x +references is preserved.
+Complexity: If &x==this, constant; otherwise +O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
+Exception safety: If &x==this, nothrow; +otherwise, basic.
+
+ +template <typename Compare> void merge(index class name& x,Compare comp); + +
+Requires: Compare is a + +Strict Weak Ordering over value_type. +Both the index and x are sorted according to comp.
+Effects: Attempts to insert every element of x into the +corresponding position of the index (according to comp). +Elements successfully inserted are erased from x. The resulting +sequence is stable, i.e. equivalent elements of either container preserve +their relative position. In the special case &x==this, no +operation is performed.
+Postconditions: Elements in the index and remaining elements in +x are sorted according to comp. +Validity of iterators to the index and of non-erased elements of x +references is preserved.
+Complexity: If &x==this, constant; otherwise +O(n + x.size()*I(n+x.size()) + x.size()*D(x.size())).
+Exception safety: If &x==this, nothrow; +otherwise, basic.
+
+ +void sort(); + +
+Requires: std::less<value_type> is a + +Strict Weak Ordering over value_type.
+Effects: Sorts the index according to +std::less<value_type>. The sorting is stable, i.e. +equivalent elements preserve their relative position.
+Postconditions: Validity of iterators and references is preserved.
+Complexity: O(n*log(n)).
+Exception safety: nothrow if +std::less<value_type> does not throw; otherwise, basic. +
+ +template <typename Compare> void sort(Compare comp); + +
+Requires: Compare is a + +Strict Weak Ordering over value_type.
+Effects: Sorts the index according to comp. The sorting +is stable, i.e. equivalent elements preserve their relative position.
+Postconditions: Validity of iterators and references is preserved.
+Complexity: O(n*log(n)).
+Exception safety: nothrow if comp does +not throw; otherwise, basic. +
+ +void reverse(); + +
+Effects: Reverses the order of the elements in the index.
+Postconditions: Validity of iterators and references is preserved.
+Complexity: O(n).
+Exception safety: nothrow. +
+ +

Special list operations

+ +

+Sequenced indices provide some convenience member functions without +counterparts in std::list. These operations are aimed at +improving the usability of sequenced indices in points where +the support offered by standard list operations is insufficient or +difficult to use. +

+ +void relocate(iterator position,iterator i); + +
+Requires: position is a valid iterator of the index. +i is a valid dereferenceable iterator of the index.
+Effects: Inserts the element pointed to by i before +position. If position==i, no operation is +performed.
+Postconditions: No iterator or reference is invalidated.
+Complexity: Constant.
+Exception safety: nothrow.
+
+ +void relocate(iterator position,iterator first,iterator last); + +
+Requires: position is a valid iterator of the index. +first and last are valid iterators of the index. +last is reachable from first. position +is not in the range [first,last).
+Effects: The range of elements [first,last) +is repositioned just before position.
+Postconditions: No iterator or reference is invalidated.
+Complexity: Constant.
+Exception safety: nothrow.
+
+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/style.css b/doc/style.css new file mode 100644 index 0000000..3e4034b --- /dev/null +++ b/doc/style.css @@ -0,0 +1,48 @@ +pre{ + BORDER-RIGHT: gray 1pt solid; + PADDING-RIGHT: 2pt; + BORDER-TOP: gray 1pt solid; + DISPLAY: block; + PADDING-LEFT: 2pt; + PADDING-BOTTOM: 2pt; + BORDER-LEFT: gray 1pt solid; + MARGIN-RIGHT: 32pt; + PADDING-TOP: 2pt; + BORDER-BOTTOM: gray 1pt solid; + FONT-FAMILY: "Courier New", Courier, mono; + background-color: #EEEEEE; +} + +table{ + PADDING-RIGHT: 2pt; + BORDER-TOP: gray 1pt solid; + DISPLAY: block; + PADDING-LEFT: 2pt; + PADDING-BOTTOM: 2pt; + BORDER-LEFT: gray 1pt solid; + MARGIN-RIGHT: 32pt; + PADDING-TOP: 2pt; + background-color: #EEEEEE; +} +td{ + BORDER-STYLE: solid; + BORDER-WIDTH: 1pt; + BORDER-LEFT: ; + BORDER-RIGHT: gray 1pt solid; + BORDER-TOP: ; + BORDER-BOTTOM: gray 1pt solid; +} +th{color: #ffffff; background-color: #000000;} +.odd_tr{background-color: #ffffff;} + +.keyword{color: #0000FF;} +.identifier{} +.comment{font-style: italic; color: #008000;} +.special{color: #800040;} +.preprocessor{color: #3F007F;} +.string{font-style: italic; color: #666666;} +.literal{font-style: italic; color: #666666;} + +.prev_link{width: 30%; float: left; text-align: left;} +.up_link{width: 39.9%; float: left; text-align: center;} +.next_link{width: 30%; float: left; text-align: right;} diff --git a/doc/tests.html b/doc/tests.html new file mode 100644 index 0000000..ec8dd5c --- /dev/null +++ b/doc/tests.html @@ -0,0 +1,146 @@ + + + + + +Boost.MultiIndex Documentation - Tests + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Tests

+ + + +
+ +
+ +

+The Boost.MultiIndex test suite exercises the whole spectrum of +functionalities provided by the library. Although the tests are not meant +to serve as a learning guide, the interested reader may find it +useful to inspect the source code to gain familiarity +with some of the least common features offered by Boost.MultiIndex. +

+ +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Boost.MultiIndex test suite.
ProgramDescription
test_basic.cppSimple program along the lines of the employees example studied in the + tutorial.
test_capacity.cppempty, size and (sequenced indices only) + resize.
test_comparison.cppComparison between indices.
test_composite_key.cppcomposite_key and composite_key_compare.
test_conv_iterators.cppChecks convertibility of constant to non-constant iterators.
test_copy_assignment.cppVarious forms of assignment: copy, operator =, insertion, + (sequenced indices only) assign . +
test_iterators.cppConstant and non-constant iterators and their reverse variants.
test_key_extractors.cppCovers all use cases of key extractors shipped with the library.
test_list_ops.cppList-like operations particular to sequenced indices.
test_modifiers.cppChecks the family of insertion and erasing operations.
test_mpl_ops.cppShows how to take advantage of mpl_index_list in a + metaprogram.
test_projection.cppProjection of iterators among indices.
test_range.cppExercises the range facility (ordered indices only).
test_safe_mode.cppComprehensive coverage of all conditions checked in safe mode.
test_set_ops.cppSet-like operations particular to ordered indices.
test_special_list_ops.cppConvenience functions of sequenced indices not present in + std::list.
test_special_set_ops.cppChecks special lookup operations using compatible sorting criteria.
test_update.cppreplace, modify and modify_key.
+

+ +
+ + + +
+ +
+ + +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/tutorial.html b/doc/tutorial.html new file mode 100644 index 0000000..729d754 --- /dev/null +++ b/doc/tutorial.html @@ -0,0 +1,1260 @@ + + + + + +Boost.MultiIndex Documentation - Tutorial + + + + +

c++boost.gif (8819 bytes)Boost.MultiIndex Tutorial

+ + + +
+ +
+ +

Contents

+ + + +

Rationale

+ +

+STL containers are designed around the concept that each container controls its +own collection of elements, giving access to them in a manner specified by the +container's type: so, an std::set maintains the elements ordered +by a specified sorting criterium, std::list allows for free +positioning of elements along a linear sequence, and so on. +

+ +

+Sometimes, the necessity arises of having different access interfaces +to the same underlying collection: for instance, some data might need to be +sorted according to more than one comparison predicate, or a bidirectional list +might benefit from a supplemental logarithmic lookup interface. In these +situations, programmers typically resort to manual compositions of different +containers, a solution that generally involves a fair amount of code +devoted to preserve the synchronization of the different parts of +the composition. Boost.MultiIndex allows for the specification of +multi_index_containers comprised of one or more indices with +different interfaces to the same collection of elements. The resulting constructs +are conceptually cleaner than manual compositions, and often perform much better. +An important design decision has been taken that the indices of a given +multi_index_container instantiation be specified at compile time: this +gives ample room for static type checking and code optimization. +

+ +

+Boost.MultiIndex takes inspiration from basic concepts of indexing arising in the +theory of relational databases, though it is not intended to provide a full-fledged +relational database framework. multi_index_container integrates seamlessly +into the STL container/algorithm design, and features some extra capabilities regarding +lookup operations and element updating which are useful extensions even for +single-indexed containers. +

+ +

+
+Fig. 1: Diagram of a multi_index_container with three indices. +

+ +

+The figure above depicts a multi_index_container composed of three indices: +the first two present a set-like interface to the elements sorted by +shape and id, respectively, while the latter index provides the functionality +of a bidirectional list in the spirit of std::list. These +indices act as "views" to the internal collection of elements, but they do not only +provide read access to the set: insertion/deletion methods are also implemented much +as those of std::sets or std::lists. Insertion of an +element through one given index will only succeed if the uniqueness constraints of all +indices are met. +

+ +

+Namespace +

+ +

+All the types of Boost.MultiIndex reside in namespace ::boost::multi_index. +Additionaly, the main class template multi_index_container and global functions +get and project are lifted to namespace ::boost +by means of using declarations. For brevity of exposition, the fragments +of code in the documentation are written as if the following declarations were in effect: +

+ +
+using namespace ::boost;
+using namespace ::boost::multi_index;
+
+ +

Introduction

+ +

+We introduce the main concepts of Boost.MultiIndex through the study of +two typical use cases. +

+ +

Multiple sorts on a single set

+ +

+STL sets and multisets are varying-length containers where elements are efficiently +sorted according to a given comparison predicate. These container classes fall short +of functionality when the programmer wishes to efficiently sort and look up the elements +following a different sorting criterium. Consider for instance: +

+ +
+struct employee
+{
+  int         id;
+  std::string name;
+
+  employee(int id,std::string name):id(id),name(name){}
+
+  bool operator<(const employee& e)const{return id<e.id;}
+};
+
+ +

The fact that IDs are unique to each employee is reflected by the way +operator< is defined, so a natural data structure for storing of +employees is just a std::set<employee>. Now, +if one wishes to print out a listing of all employees in alphabetical order, available +solutions may have disadvantages either in terms of storage space, complexity or ease +of maintenance: +

    +
  • Copy the employee set into a vector or similar and sort this by a comparison +functor dependent upon employee::name. +
  • Keep a secondary data structure of pointers to the elements of the set, appropriately +sorted by name. +
+Of these, probably the second solution will be the one adopted by most programmers +concerned about efficiency, but it imposes the annoying burden of keeping the two +structures in sync. If the code is additionally required to be exception-safe, this +construct easily becomes unmaintainable. +

+ +

+Boost.MultiIndex features ordered indices, which +sort the elements according to a particular key, and are designed to help programmers +in need of sequences of elements for which more than one sorting criteria are +relevant. We do so by defining a multi_index_container +instantiation composed of several ordered indices: each index, viewed in isolation, +behaves much as an ordered std::set (or std::multiset), whilst +the overall integrity of the entire data structure is preserved. Our example problem +thus can be solved with Boost.MultiIndex as follows: +

+ +
+// define a multiply indexed set with indices by id and name
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    // sort by employee::operator<
+    ordered_unique<identity<employee> >,
+    
+    // sort by less<string> on name
+    ordered_non_unique<member<employee,std::string,&employee::name> >
+  > 
+> employee_set;
+
+void print_out_by_name(const employee_set& es)
+{
+  // get a view to index #1(name)
+  const employee_set::nth_index<1>::type& name_index=es.get<1>();
+  // use name_index as a regular std::set
+  std::copy(
+    name_index.begin(),name_index.end(),
+    std::ostream_iterator<employee>(std::cout));
+}
+
+ +

+Instead of a single comparison predicate type, as it happens for STL associative +containers, multi_index_container is passed a typelist of index +specifications (indexed_by), each one inducing the corresponding index. +Indices are accessed via +get<N>() +where N ranges between 0 and the number of comparison +predicates minus one. The functionality of index #0 can be accessed directly from an +multi_index_container object without using get<0>(): for instance, +es.begin() is equivalent to es.get<0>().begin(). +

+ +

A bidirectional list with fast lookup

+ +

+This study case allows us to introduce so-called +sequenced indices, and how they +interact with ordered indices to construct powerful containers. Suppose +we have a text parsed into words and stored in a list like this: +

+ +
+typedef std::list<std::string> text_container;
+
+std::string text=
+  "Alice was beginning to get very tired of sitting by her sister on the "
+  "bank, and of having nothing to do: once or twice she had peeped into the "
+  "book her sister was reading, but it had no pictures or conversations in "
+  "it, 'and what is the use of a book,' thought Alice 'without pictures or "
+  "conversation?'";
+
+// feed the text into the list
+text_container tc;
+boost::tokenizer<boost::char_separator<char> > tok
+  (text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
+std::copy(tok.begin(),tok.end(),std::back_inserter(tc));
+
+ +

+If we want to count the occurrences of a given word into the text we will resort +to std::count: +

+ +
+std::size_t occurrences(const std::string& word)
+{
+  return std::count(tc.begin(),tc.end(),word);
+}
+
+ +

+But this implementation of occurrences performs in linear time, which +could be unacceptable for large quantities of text. Similarly, other operations like +deletion of selected words are just too costly to carry out on a +std::list: +

+ +
+void delete_word(const std::string& word)
+{
+  tc.remove(word); // scans the entire list looking for word
+}
+
+ +

+When performance is a concern, we will need an additional data structure that indexes +the elements in tc, presumably in alphabetical order. Boost.MultiIndex +does precisely this through the combination of sequenced and ordered indices: +

+ +
+// define a multi_index_container with a list-like index and an ordered index
+typedef multi_index_container<
+  std::string,
+  indexed_by<
+    sequenced<>, // list-like index
+    ordered_non_unique<identity<std::string> > // words by alphabetical order
+  >
+> text_container;
+
+std::string text=...
+
+// feed the text into the list
+text_container tc;
+boost::tokenizer<boost::char_separator<char> > tok
+  (text,boost::char_separator<char>(" \t\n.,;:!?'\"-"));
+std::copy(tok.begin(),tok.end(),std::back_inserter(tc));
+
+ +

+So far, the substitution of multi_index_container for std::list +does not show any advantage. The code for inserting the text into the container +does not change as sequenced indices provide an interface similar to that of +std::list (no explicit access to this index through +get<0>() is needed as multi_index_container inherits the +functionality of index #0.) But the specification of an additional ordered index +allows us to implement occurrences and delete_word +in a much more efficient manner: +

+ +
+std::size_t occurrences(const std::string& word)
+{
+  // get a view to index #1
+  text_container::nth_index<1>::type& sorted_index=tc.get<1>();
+
+  // use sorted_index as a regular std::set
+  return sorted_index.count(word);
+}
+
+void delete_word(const std::string& word)
+{
+  // get a view to index #1
+  text_container::nth_index<1>::type& sorted_index=tc.get<1>();
+
+  // use sorted_index as a regular std::set
+  sorted_index.erase(word);
+}
+
+ +

+Now, occurrences and delete_word have logarithmic +complexity. The programmer can use index #0 for accessing the text as with +std::list, and use index #1 when logarithmic lookup is needed. +

+ +

+Index specification +

+ +

+The indices of a multi_index_container instantiation are specified by +means of the +indexed_by construct. For instance, the instantiation +

+ +
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    ordered_unique<identity<employee> >,
+    ordered_non_unique<member<employee,std::string,&employee::name> >
+  > 
+> employee_set;
+
+ +

+is comprised of a unique ordered index and a +non-unique ordered index, while in +

+ +
+typedef multi_index_container<
+  std::string,
+  indexed_by<
+    sequenced<>,
+    ordered_non_unique<identity<std::string> >
+  >
+> text_container;
+
+ +

+we specifiy two indices, the first of sequenced type, +the second a non-unique ordered index. In general, we +can specify an arbitrary number of indices: each of the arguments of +indexed_by is called an +index specifier. +Depending on the type of index being specified, the corresponding specifier +will need additional information: for instance, the specifiers ordered_unique +and ordered_non_unique are provided with a +key extractor and an optional +comparison predicate which jointly indicate +how the sorting of elements will be performed. +

+ +

+A multi_index_container instantiation can be declared without supplying +the indexed_by part: in this case, default index values are taken +so that the resulting type is equivalent to a regular std::set. +Concretely, the instantiation +

+ +
+multi_index_container<(element)>
+
+ +

+is equivalent to +

+ +
+multi_index_container<
+  (element),
+  indexed_by<
+    ordered_unique<identity<(element)> >
+  >
+>
+
+ +

Tagging

+ +

+In order to retrieve (a reference to) an index of a given multi_index_container, +the programmer must provide its order number, which is cumbersome and not very +self-descriptive. Optionally, indices can be assigned tags (C++ types) that +act as more convenient mnemonics. If provided, tags must be passed as the +first parameter of the corresponding index specifier. The following is a revised version of +employee_set with inclusion of tags: +

+ +
+// tags 
+struct name{};
+
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    ordered_unique<identity<employee> >,
+    ordered_non_unique<tag<name>,member<employee,std::string,&employee::name> >
+  >
+> employee_set;
+
+ +

+Tags have to be passed inside the tag construct. Any type can be +used as a tag for an index, although in general one will choose names that are +descriptive of the index they are associated with. The tagging mechanism allows +us to write expressions like

+ +
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name::iterator it=es.get<name>().begin();
+
+ +

+If no tag is provided for an index (as is the case for index #0 of the previous +example), access to that index can only be performed by number. Note the existence +of two different typedefs nth_index and +index for referring to an index by number and by tag, respectively; +for instance, +

    +
  • employee_set::nth_index<1>::type is the type of + index #1,
  • +
  • employee_set::index<name>::type is the type of the index + tagged with name (the same index #1 in this case.)
  • +
+get(), on the other hand, is overloaded to serve both styles of access: +

+ +
+employee_set::index<name>::type& name_index=es.get<name>();
+employee_set::nth_index<1>::type& name_index2=es.get<1>(); // same index
+
+ +

+Additionally, the tag class template accepts several tags for one +index, that we can use interchangeably: for instance, the specification of index #1 +in the previous example can be rewritten to hold two different tags +name and by_name: +

+ +
+// tags
+struct name{};
+struct by_name{};
+
+typedef multi_index_container<
+  ...
+    ordered_non_unique<
+      tag<name,by_name>,
+      member<employee,std::string,&employee::name>
+    >
+  ...
+> employee_set;
+
+ +

+Index types +

+ +

+Currently, Boost.MultiIndex provides the following index types: +

    +
  • Ordered indices sort the elements like std::sets do and + provide a similar interface. There are unique and non-unique + variants: the former do not allow for duplicates, while the latter permit + them (like std::multiset.)
  • +
  • Sequenced indices are modeled after the semantics and interface of + std::list: they arrange the elements as if in a bidirectional + list.
  • +
+The examples in the introduction exercise all of these +indices. +

+ +

+Ordered indices +

+ +

+Ordered indices sort the elements in a multi_index_container according +to a specified key and an associated comparison predicate. These indices can +be viewed as analogues of the standard container std::set, and in fact +they do replicate its interface, albeit with some minor differences dictated +by the general constraints of Boost.MultiIndex. +

+ +

+Unique and non-unique variants +

+ +

+Ordered indices are classified into unique, which prohibit two +elements to have the same key value, and non-unique indices, +which allow for duplicates. Consider again the definition +

+ +
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    ordered_unique<identity<employee> >,
+    ordered_non_unique<member<employee,std::string,&employee::name> >
+  > 
+> employee_set;
+
+ +

+In this instantiation of multi_index_container, the first index is to be +treated as unique (since IDs are exclusive to each employee) and thus is declared using +ordered_unique, whereas the second index is non-unique (as the possibility exists +that say two John Smiths are hired in the same company), which is specified by the use +of ordered_non_unique. +

+ +

+The classification of ordered indices in unique and non-unique has an impact on which +elements are allowed to be inserted into a given multi_index_container; briefly put, +unique ordered indices mimic the behavior of std::sets while non-unique +ordered indices are similar to std::multisets. For instance, an +employee_set can hold the objects employee(0,"George Brown") +and employee(1,"George Brown"), but will not accept the insertion of an +employee object whose ID coincides with that of some previously inserted +employee. +

+ +

+More than one unique index can be specified. For instance, if we augment +employee to include an additional member for the Social Security number, +which is reasonably treated as unique, the following captures this design: +

+ +
+struct employee
+{
+  int         id;
+  std::string name;
+  int         ssnumber;
+
+  employee(int id,std::string name,int ssnumber):
+    id(id),name(name),ssnumber(ssnumber){}
+
+  bool operator<(const employee& e)const{return id<e.id;}
+};
+
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    // sort by employee::operator<
+    ordered_unique<identity<employee> >,
+    
+    // sort by less<string> on name
+    ordered_non_unique<member<employee,std::string,&employee::name> >,
+    
+    // sort by less<int> on ssnumber
+    ordered_unique<member<employee,int,&employee::ssnumber> >
+  >
+> employee_set;
+
+ +

+Specification +

+ +

+Ordered index specifiers in indexed_by must conform to one of the +following syntaxes: +

+ +
+(ordered_unique | ordered_non_unique)
+  <[(tag)[,(key extractor)[,(comparison predicate)]]]>
+
+(ordered_unique | ordered_non_unique)
+  <[(key extractor)[,(comparison predicate)]]>
+
+ +

+The first optional argument is used if tags are associated +with the index. We now proceed to briefly discuss the remaining arguments +of an ordered index specifier. +

+ +

+Key extraction +

+ +

+The first template parameter (or the second, if tags are supplied) +in the specification of an ordered index provides a key extraction predicate. +This predicate takes a whole element (in our example, a reference to an +employee object) and returns the piece of information by which +the sorting is performed. In most cases, one of the following two situations arises: +

    +
  • The whole element serves as the key, as is the case of the first index +in employee_set. The predefined +identity predicate +can be used here as a key extractor; identity returns as the key the +same object passed as argument.
  • +
  • The comparison is performed on a particular data member of the element; this +closely follows the specification of indices on a column of a table in relational +databases. Boost.MultiIndex provides +member, which returns +as the key a member of the element specified by a given pointer.
  • +
+As an example, consider again the definition of employee_set. The +definition of the first index: +

+ +
+ordered_unique<identity<employee> >
+
+ +

+specifies by means of identity that element +objects themselves serve as key for this index. On the other hand, in the second +index: +

+ +
+ordered_non_unique<member<employee,std::string,&employee::name> >
+
+ +

+we use member to extract the name part of the +employee object. The key type of this index is then +std::string. +

+ +

+Another common situation arises when the sorting is performed on the result +of a particular member function. This resembles the notion of +calculated indices supported by some relational databases. +In these cases, the key is not a data member of the element, but rather it is +a value returned by a particular member function. Boost.MultiIndex supports this +kind of key extraction through +const_mem_fun. +Consider the following extension of our example where sorting on the third index +is based upon the length of the name field: +

+ +
+struct employee
+{
+  int         id;
+  std::string name;
+
+  employee(int id,std::string name,int ssnumber):id(id),name(name){}
+
+  bool operator<(const employee& e)const{return id<e.id;}
+
+  // returns the length of the name field
+  std::size_t name_length()const{return name.size();}
+};
+
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    // sort by employee::operator<
+    ordered_unique<identity<employee> >,
+    
+    // sort by less<string> on name
+    ordered_non_unique<member<employee,std::string,&employee::name> >,
+    
+    // sort by less<int> on name_length()
+    ordered_non_unique<
+      const_mem_fun<employee,std::size_t,&employee::name_length>
+    >
+  >
+> employee_set;
+
+ +

Example 2 in the examples section +provides a complete program showing how to use const_mem_fun. +Almost always you will want to use a const member function, +since elements in a multi_index_container are treated as constant, much +as elements of an std::set. However, a +mem_fun +counterpart is provided for use with non-constant member functions, whose +applicability is discussed on the paragraph on +advanced features +of Boost.MultiIndex key extractors in the advanced topics section. +

+ +

+identity, member and const_mem_fun serve +most common situations in the design of a multi_index_container. However, the +user is free to provide her own key extractors in more exotic situations, as long as +these conform to the Key +Extractor concept. For instance, +example 6 implements several key +extraction techniques called for when elements and/or keys are accessed via +pointers. +

+ +

Comparison predicates

+ +

+The last part of the specification of an ordered index is the associated +comparison predicate, which must order the keys in a less-than fashion. +These comparison predicates are not different from those used by STL containers like +std::set. By default (i.e. if no comparison predicate is provided), +an index with keys of type key_type sorts the elements by +std::less<key_type>. Should other comparison criteria be needed, +they can be specified as an additional parameter in the index declaration: +

+ +
+// define a multiply indexed set with indices by id and by name
+// in reverse alphabetical order
+typedef multi_index_container<
+  employee,
+  indexed_by<
+    ordered_unique<identity<employee> >, // as usual
+    ordered_non_unique<
+      member<employee,std::string,&employee::name>,
+      std::greater<std::string>  // default would be std::less<std::string>
+    >
+  >
+> employee_set;
+
+ +

Special lookup operations

+ +

+A given ordered index allows for lookup based on its key type, rather than the +whole element. For instance, to find Veronica Cruz in an +employee_set one would write: +

+ +
+employee_set es;
+...
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name::iterator it=es.get<name>().find("Veronica Cruz");
+
+ +

As a plus, Boost.MultiIndex provides lookup operations accepting search keys +different from the key_type of the index, which is a specially useful +facility when key_type objects are expensive to create. Ordered STL containers +fail to provide this functionality, which often leads to inelegant workarounds: consider for +instance the problem of determining the employees whose IDs fall in the range [0,100]. Given +that the key of employee_set index #0 +is employee itself, on a first approach one would write the following: +

+ +
+employee_set::iterator p0=es.lower_bound(employee(0,""));
+employee_set::iterator p1=es.upper_bound(employee(100,""));
+
+ +

+Note however that std::less<employee> actually only depends +on the IDs of the employees, so it would be more convenient to avoid +the creation of entire employee objects just for the sake of +their IDs. Boost.MultiIndex allows for this: define an appropriate +comparison predicate +

+ +
+struct comp_id
+{
+  // compare an ID and an employee
+  bool operator()(int x,const employee& e2)const{return x<e2.id;}
+
+  // compare an employee and an ID
+  bool operator()(const employee& e1,int x)const{return e1.id<x;}
+};
+
+ +

and now write the search as

+ +
+employee_set::iterator p0=es.lower_bound(0,comp_id());
+employee_set::iterator p1=es.upper_bound(100,comp_id());
+
+ +

+Here we are not only passing IDs instead of employee objects: +an alternative comparison predicate is passed as well. In general, lookup operations +of ordered indices are overloaded to accept +compatible sorting +criteria. The somewhat cumbersone definition of compatibility in this context +is given in the reference, but roughly speaking we say that a comparison predicate +C1 is compatible with C2 if any sequence sorted by +C2 is also sorted with respect to C1. +The following shows a more interesting use of compatible predicates: +

+ +
+// sorting by name's initial
+struct comp_initial
+{
+  bool operator()(char ch,const std::string& s)const{
+    if(s.empty())return false;
+    return ch<s[0];
+  }
+
+  bool operator()(const std::string& s,char ch)const{
+    if(s.empty())return true;
+    return s[0]<ch;
+  }
+};
+
+// obtain first employee whose name begins with 'J' (ordered by name)
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>(); 
+employee_set_by_name::const_iterator it=
+  name_index.lower_bound('J',comp_initial());
+
+ +

Retrieval of ranges

+ +

+Range searching, i.e. the lookup of all elements in a given interval, is a very +frequent operation for which standard lower_bound and +upper_bound can be resorted to, though in a cumbersome manner. +For instance, the following code retrieves the elements of an +multi_index_container<double> in the interval [100,200]: +

+ +
+typedef multi_index_container<double> double_set;
+// note: default template parameters resolve to
+// multi_index_container<double,indexed_by<unique<identity<double> > > >.
+
+typedef double_set s;
+...
+double_set::iterator it0=s.lower_bound(100.0);
+double_set::iterator it1=s.upper_bound(200.0);
+// range [it0,it1) contains the elements in [100,200]
+
+ +

+Subtle changes to the code are required when strict inequalities are considered. +To retrieve the elements greater than 100 and less than 200, the +code has to be rewritten as +

+ +
+double_set::iterator it0=s.upper_bound(100.0);
+double_set::iterator it1=s.lower_bound(200.0);
+// range [it0,it1) contains the elements in (100,200)
+
+ +

+To add to this complexity, the careful programmer has to take into account +that the lower and upper bounds of the interval searched be compatible: for +instance, if the lower bound is 200 and the upper bound is 100, the iterators +it0 and it1 produced by the code above will be in reverse +order, with possibly catastrophic results if a traversal from it0 +to it1 is tried. All these details make range searching a tedious +and error prone task. +

+ +

+The range +member function, often in combination with +Boost.Lambda expressions, can +greatly help alleviate this situation: +

+ +
+using namespace boost::lambda;
+
+typedef multi_index_container<double> double_set;
+typedef double_set s;
+...
+std::pair<double_set::iterator,double_set::iterator> p=
+  s.range(100.0<=_1,_1<=200); // 100<= x <=200
+...
+p=s.range(100.0<_1,_1<200);   // 100<  x < 200
+...
+p=s.range(100.0<=_1,_1<200);  // 100<= x < 200
+
+ +

+range simply accepts predicates specifying the lower and upper bounds +of the interval searched. Please consult the reference for a detailed explanation +of the permissible predicates passed to range.

+ +

+One or both bounds can be omitted with the special unbounded marker: +

+ +
+p=s.range(100.0<=_1,unbounded); // 100 <= x
+p=s.range(unbounded,_1<200.0);  //   x <  200
+p=s.range(unbounded,unbounded); // equiv. to std::make_pair(s.begin(),s.end())
+
+ +

Updating

+ +

+The replace member function +performs in-place replacement of a given element as the following example shows: +

+ +
+typedef index<employee_set,name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>();
+
+employee_set_by_name::iterator it=name_index.find("Anna Jones");
+employee anna=*it;
+anna.name="Anna Smith";      // she just got married to Calvin Smith
+name_index.replace(it,anna); // update her record
+
+ +

+replace performs this substitution in such a manner that: +

    +
  • The complexity is constant time if the changed element retains its original +order with respect to all indices; it is logarithmic otherwise. +
  • Iterator and reference validity are preserved. +
  • The operation is strongly exception-safe, i.e. the multi_index_container +remains unchanged if some exception (originated by the system or the user's data +types) is thrown. +
+replace is a powerful operation not provided by standard STL +containers, and one that is specially handy when strong exception-safety is +required. +

+ +

+The observant reader might have noticed that the convenience of replace +comes at a cost: namely the whole element has to be copied twice to do +the updating (when retrieving it and inside replace). If elements +are expensive to copy, this may be quite a computational cost for the modification +of just a tiny part of the object. To cope with this situation, Boost.MultiIndex +provides an alternative updating mechanism called +modify: +

+ +
+struct change_name
+{
+  change_name(const std::string& new_name):new_name(new_name){}
+
+  void operator()(employee& e)
+  {
+    e.name=new_name;
+  }
+
+private:
+  std::string new_name;
+};
+...
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>();
+
+employee_set_by_name::iterator it=name_index.find("Anna Jones");
+name_index.modify(it,change_name("Anna Smith"));
+
+ +

modify accepts a functor (or pointer to function) that is +passed a reference to the element to be changed, thus eliminating the need +for spurious copies. Like replace, modify does preserve +the internal orderings of all the indices of the multi_index_container. +However, the semantics of modify is not entirely equivalent to +replace. Consider what happens if a collision occurs as a result +of modifying the element, i.e. the modified element clashes with another with +respect to some unique ordered index. In the case of replace, the +original value is kept and the method returns without altering the container, but +modify cannot afford such an approach, since the modifying functor +leaves no trace of the previous value of the element. Integrity constraints +thus lead to the following policy: when a collision happens in the +process of calling modify, the element is erased and the method returns +false. This difference in behavior between replace and +modify has to be considered by the programmer on a case-by-case basis. +

+ +

+A key-based version of modify, named +modify_key, is +provided as well. In this case, the modifying functor is passed a reference to +the key_value part of the element instead of the whole object. Note +that modify_key cannot be used for key extractors which return calculated +values instead of references to data members of the elements, such +as const_mem_fun. +

+ +
+struct change_str
+{
+  change_str(const std::string& new_str):new_str(new_str){}
+
+  // note this is passed a string, not an employee
+  void operator()(std::string& str)
+  {
+    str=new_str;
+  }
+
+private:
+  std::string new_str;
+};
+...
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>();
+
+employee_set_by_name::iterator it=name_index.find("Anna Jones");
+name_index.modify_key(it,change_str("Anna Smith"));
+
+ +

+Just as modify does, modify_key erases the element if +the modification results in collisions in some index. modify and +modify_key are particularly well suited to use in conjunction to +Boost.Lambda +for defining the modifying functors: +

+ +
+using namespace boost::lambda;
+
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>();
+
+employee_set_by_name::iterator it=name_index.find("Anna Jones");
+name_index.modify_key(it,_1="Anna Smith");
+
+ +

+Sequenced indices +

+ +

+Unlike ordered indices, sequenced indices do not impose a fixed order on the +elements: instead, these can be arranged in any position on the sequence, in the +same way as std::list permits. The interface of sequenced indices +is thus designed upon that of std::list; nearly every operation +provided in the standard container is replicated here, occasionally with changes +in the syntax and/or semantics to cope with the constraints imposed by +Boost.MultiIndex. In particular, there is an important limitation of sequenced +indices with respect to std::lists, namely that elements of an +multi_index_container are not mutable through an iterator: +

+ +
+multi_index_container<
+  int,
+  indexed_by<sequenced<> >
+> s;             // list-like container
+
+s.push_front(0);
+*(s.begin())==1; // ERROR: the element cannot be changed
+
+ +

+That is, iterators of a sequenced index (of all types of indices, actually) +point to constant elements. This limitation might come as a surprise, but +it is imposed by the way multi_index_containers work; if elements were +allowed to be changed in this manner, we could introduce inconsistencies +in other ordered indices of the multi_index_container. Element modification +can nevertheless be done by means of +update operations. +

+ +

+Consider a multi_index_container with two or more indices, one of them +of sequenced type. If an element is inserted through another index, +then it will be automatically appended to the end of the sequenced index. +An example will help to clarify this: +

+ +
+multi_index_container<
+  int,
+  indexed_by<
+    sequenced<>,           // sequenced type
+    ordered_unique<identity<int> > // another index
+  >
+> s;
+
+s.get<1>().insert(1); // insert 1 through index #1
+s.get<1>().insert(0); // insert 0 through index #1
+
+// list elements through sequenced index #0
+std::copy(s.begin(),s.end(),std::ostream_iterator<int>(std::cout));
+
+// result: 1 0
+
+ +

+Thus the behavior of sequenced indices when insertions are not made through +them is to preserve insertion order. +

+ +

Specification

+ +

+Sequenced indices are specified with the sequenced construct: +

+ +
+sequenced<[(tag)]>
+
+ +

+The tag parameter is optional. +

+ +

List operations

+ +

+As mentioned before, sequenced indices mimic the interface of +std::list, and most of the original operations therein are +provided as well. The semantics and complexity of these operations, however, +do not always coincide with those of the standard container. Differences +result mainly from the fact that insertions into a sequenced index are not +guaranteed to succeed, due to the possible banning by other indices +of the multi_index_container. Consult the +reference for further details. +

+ +

Updating

+ +

+Like ordered indices, sequenced indices provide +replace and +modify +operations, with identical functionality. There is however no analogous +modify_key, since sequenced indices are not key-based. +

+ +

Projection of iterators

+ +

+Given indices i1 and i2 on the same multi_index_container, +project can be used to +retrieve an i2-iterator from an i1-iterator, both of them +pointing to the same element of the set. This functionality allows the programmer to +move between different indices of the same multi_index_container when performing +elaborate operations: +

+ +
+typedef employee_set::index<name>::type employee_set_by_name;
+employee_set_by_name& name_index=es.get<name>();
+
+// list employees by ID starting from Robert Brown's ID
+
+employee_set_by_name::iterator it1=name_index.find("Robert Brown");
+
+// obtain an iterator of index #0 from it1
+employee_set::iterator it2=es.project<0>(it1); 
+
+std::copy(it2,es.end(),std::ostream_iterator<employee>(std::cout));
+
+ +

+A slightly more interesting example: +

+ +
+text_container tc;
+
+// get a view to index #1 (ordered index on the words)
+text_container::nth_index<1>::type& sorted_index=tc.get<1>();
+
+// prepend "older" to all occurrences of "sister"
+
+text_container::nth_index_iterator<1>::type it1=
+  sorted_index.lower_bound("sister");
+  
+while(it1!=sorted_index.end()&&*it1=="sister"){
+  // convert to an iterator to the sequenced index
+  text_container::iterator it2=tc.project<0>(it1);
+
+  tc.insert(it2,"older");
+  ++it1;
+}
+
+ +

+When provided, project can also be used with +tags. +

+ +

Complexity and exception safety

+ +

+multi_index_container provides the same complexity and exception safety +guarantees as the equivalent STL containers do. Iterator and reference validity +is preserved in the face of insertions, even for replace and modify operations. +

+ +

+Appropriate instantiations of multi_index_container can in fact simulate +std::set, std::multiset and (with more limitations) +std::list, as shown in the +advanced topics +section. These simulations are as nearly as efficient as the original STL +containers; consult the reference for further +information on complexity guarantees and the +performance section for practical measurements of +efficiency. +

+ +
+ + + +
+ +
+ +

Revised May 7th 2004

+ +

Copyright © 2003-2004 Joaquín M López Muñoz. +Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt) +

+ + + diff --git a/doc/up.gif b/doc/up.gif new file mode 100644 index 0000000000000000000000000000000000000000..acb3777770f86aaa1172eae92255d962d5ed6edc GIT binary patch literal 853 zcmZ?wbhEHb6krfw_|5>L~%oSd9oTwL7T+&nxy zyu7@8e0==;`~m_3f`WoVLPEmA!XhFfqN1W=Vq)Ur;t~=Pl9G~AQc}{=(lRnKva+&r za&q$W@(KzHii(O#N=nMg$|@=KYmvnwpwgT3XuL+B!Nqy1Kf0dV2c$ z`UVCDhK7blMn=ZQ#wI2vrlzK5W@hH*<`xzfmX?-QR#w*5);2aawzjr*c6Rpm_6`mX zj*gB_PEO9w&Mq!4uCA_bZf@@G?j9ZO-;?s%`Gi0t*xzX zZEfxC?HwH*ot>RsU0vPX-90@$y}iACeSQ7?{Szikm^g9bq)C$|Po6ww%9N>7r%szT zZTj@-GiJ<~IdkT$S+i!(o;_#IoVj!7&YL%H{`~n17A#n}aN(jwixw|lykyCerAwDC zTefWZ^5rX5tXR2n<*HSyRoH%*%du|Ns9wpd0|o3k)1q z4F5T0e(*CeG;s(@`JA}$z^RQzR<1=O;E{8On7Y}S6@iM!du6P{cr+(1IozRUy38iB O;Hl$u16?*225SKAsFtPx literal 0 HcmV?d00001 diff --git a/example/Jamfile b/example/Jamfile new file mode 100644 index 0000000..c036f5e --- /dev/null +++ b/example/Jamfile @@ -0,0 +1,44 @@ +# Boost.MultiIndex examples Jamfile +# +# Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and distribution +# are subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/libs/multi_index for library home page. + +subproject libs/multi_index/example ; + +exe basic + : basic.cpp + : $(BOOST_ROOT) + ; + +exe bimap + : bimap.cpp + : $(BOOST_ROOT) + ; + +exe complex_structs + : complex_structs.cpp + : $(BOOST_ROOT) + ; + +exe composite_keys + : composite_keys.cpp + : $(BOOST_ROOT) + ; + +exe memfun_key + : memfun_key.cpp + : $(BOOST_ROOT) + ; + +exe non_default_ctor + : non_default_ctor.cpp + : $(BOOST_ROOT) + ; + +exe sequenced + : sequenced.cpp + : $(BOOST_ROOT) + ; diff --git a/example/basic.cpp b/example/basic.cpp new file mode 100644 index 0000000..3e2beff --- /dev/null +++ b/example/basic.cpp @@ -0,0 +1,122 @@ +/* Boost.MultiIndex basic example. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#if !defined(NDEBUG) +#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING +#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE +#endif + +#include +#include +#include +#include +#include +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; + +/* an employee record holds its ID, name and age */ + +struct employee +{ + int id; + std::string name; + int age; + + employee(int id_,std::string name_,int age_):id(id_),name(name_),age(age_){} + + friend std::ostream& operator<<(std::ostream& os,const employee& e) + { + os<, BOOST_MULTI_INDEX_MEMBER(employee,int,id)>, + ordered_non_unique< + tag,BOOST_MULTI_INDEX_MEMBER(employee,std::string,name)>, + ordered_non_unique< + tag, BOOST_MULTI_INDEX_MEMBER(employee,int,age)> > +> employee_set; + +template +void print_out_by( + const MultiIndexContainer& s, + Tag* =0 /* fixes a MSVC++ 6.0 bug with implicit template function parms */ +) +{ + /* obtain a reference to the index tagged by Tag */ + + const typename boost::multi_index::index::type& i= + get(s); + + typedef typename MultiIndexContainer::value_type value_type; + + /* dump the elements of the index to cout */ + + std::copy(i.begin(),i.end(),std::ostream_iterator(std::cout)); +} + + +int main() +{ + employee_set es; + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + + /* next insertion will fail, as there is an employee with + * the same ID + */ + + es.insert(employee(2,"Aristotle",2387)); + + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + /* list the employees sorted by ID, name and age */ + + std::cout<<"by ID"<(es); + std::cout<(es); + std::cout<(es); + std::cout< +#include +#include +#include +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; + +/* tags for accessing both sides of a bidirectional map */ + +struct from{}; +struct to{}; + +/* The class template bidirectional_map wraps the specification + * of a bidirectional map based on multi_index_container. + */ + +template +struct bidirectional_map +{ + typedef std::pair value_type; + +#if defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS) ||\ + defined(BOOST_MSVC)&&(BOOST_MSVC<1300) ||\ + defined(BOOST_INTEL_CXX_VERSION)&&defined(_MSC_VER)&&\ + (BOOST_INTEL_CXX_VERSION<=700) + +/* see Advanced topics: Use of member_offset for info on member<> and + * member_offset<> + */ + + BOOST_STATIC_CONSTANT(unsigned,from_offset=offsetof(value_type,first)); + BOOST_STATIC_CONSTANT(unsigned,to_offset =offsetof(value_type,second)); + + typedef multi_index_container< + value_type, + indexed_by< + ordered_unique< + tag,member_offset >, + ordered_unique< + tag, member_offset > + > + > type; + +#else + + /* A bidirectional map can be simulated as a multi_index_container + * of pairs of (FromType,ToType) with two unique indices, one + * for each member of the pair. + */ + + typedef multi_index_container< + value_type, + indexed_by< + ordered_unique< + tag,member >, + ordered_unique< + tag, member > + > + > type; + +#endif +}; + +/* a dictionary is a bidirectional map from strings to strings */ + +typedef bidirectional_map::type dictionary; + +int main() +{ + dictionary d; + + /* Fill up our microdictionary. first members Spanish, second members + * English. + */ + + d.insert(dictionary::value_type("hola","hello")); + d.insert(dictionary::value_type("adios","goodbye")); + d.insert(dictionary::value_type("rosa","rose")); + d.insert(dictionary::value_type("mesa","table")); + + + std::cout<<"enter a word"< and family instead */ + + dictionary::iterator it=get(d).find(word); + if(it!=d.end()){ + std::cout<second<<" in English"<::type it2=get<1>(d).find(word); + if(it2!=get<1>(d).end()){ + std::cout<first<<" in Spanish"<().find(word); + if(it!=d.end()){ /* found */ + + /* the second part of the element is the equivalent in English */ + + std::cout<second<<" in English"<::type it2=d.get().find(word); + if(it2!=d.get().end()){ + std::cout<first<<" in Spanish"< +#include +#include +#include +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; + +/* This small utility that cascades two key extractors will be + * used througout the example. + */ + +template +struct key_from_key +{ +public: + typedef typename KeyExtractor1::result_type result_type; + + key_from_key( + const KeyExtractor1& key1_=KeyExtractor1(), + const KeyExtractor2& key2_=KeyExtractor2()): + key1(key1_),key2(key2_) + {} + + template + result_type operator()(Arg& arg)const + { + return key1(key2(arg)); + } + +private: + KeyExtractor1 key1; + KeyExtractor2 key2; +}; + +/* tags for accessing several indices defined below */ + +struct model{}; +struct manufacturer{}; +struct price{}; + +/* a manufacturer struct just holds the name of the manufacturer */ + +struct car_manufacturer +{ + std::string name; + + car_manufacturer(const std::string& name_):name(name_){} +}; + +/* car_model holds the model of car, its price and a pointer to the + * manufacturer. The pointer thing eliminates duplication of the same + * data among cars of the same manufacturer. + */ + +struct car_model +{ + std::string model; + const car_manufacturer* manufacturer; + int price; + + car_model( + const std::string& model_,const car_manufacturer* manufacturer_,int price_): + model(model_),manufacturer(manufacturer_),price(price_) + {} + + friend std::ostream& operator<<(std::ostream& os,const car_model& c) + { + os<name<<" "< does the work for us.) + */ + +typedef multi_index_container< + car_manufacturer, + indexed_by< + ordered_unique< + BOOST_MULTI_INDEX_MEMBER(car_manufacturer,std::string,name) + > + > +> car_manufacturer_table; + +/* Define a multi_index_container of car_models with following indices: + * - a unique index sorted by car_model::model, + * - a non-unique index sorted by car_model::manufacturer; note the + * non-standard manufacturer_extractor used. + * - a non-unique index sorted by car_model::price. + */ + +typedef multi_index_container< + car_model, + indexed_by< + ordered_unique< + tag,BOOST_MULTI_INDEX_MEMBER(car_model,std::string,model) + >, + ordered_non_unique< + tag, + key_from_key< + BOOST_MULTI_INDEX_MEMBER(car_manufacturer,const std::string,name), + BOOST_MULTI_INDEX_MEMBER( + car_model,const car_manufacturer *,manufacturer) + > + >, + ordered_non_unique< + tag,BOOST_MULTI_INDEX_MEMBER(car_model,int,price) + > + > +> car_table; + +/* We call a *view* to a multi_index_container storing pointers instead of + * actual objects. These views are used in the complex search performed + * in the program. Resorting to multi_index of pointers eliminates + * unnecessary copying of objects, and provides us with an opportunity + * to show how BOOST_MULTI_INDEX_MEMBER can be used with pointer + * type elements. + * car_table_price_view indexes (pointers to) car_models by price. + */ + +typedef multi_index_container< + const car_model*, + indexed_by< + ordered_non_unique + > +> car_table_price_view; + +/* car_table_manufacturer_view indexes (pointers to) car_models by + * manufacturer + */ + +typedef multi_index_container< + const car_model*, + indexed_by< + ordered_non_unique< + key_from_key< + BOOST_MULTI_INDEX_MEMBER(car_manufacturer,const std::string,name), + BOOST_MULTI_INDEX_MEMBER( + car_model,const car_manufacturer * const,manufacturer) + > + > + > +> car_table_manufacturer_view; + +int main() +{ + car_manufacturer_table cmt; + + /* Fill the car_manufacturer table and keep pointers to the + * elements inserted. + */ + + const car_manufacturer * cadillac= + &*(cmt.insert(car_manufacturer("Cadillac")).first); + const car_manufacturer * ford = + &*(cmt.insert(car_manufacturer("Ford")).first); + const car_manufacturer * bmw = + &*(cmt.insert(car_manufacturer("BMW")).first); + const car_manufacturer * audi = + &*(cmt.insert(car_manufacturer("Audi")).first); + + car_table ct; + + /* Fill the car_model_table. We use the previously initialized + * pointers to the elements of cmt. + */ + + ct.insert(car_model("XLR",cadillac,76200)); + ct.insert(car_model("SRX",cadillac,38690)); + ct.insert(car_model("CTS",cadillac,30695)); + ct.insert(car_model("Escalade",cadillac,54770)); + ct.insert(car_model("ESV",cadillac,57195)); + ct.insert(car_model("EXT",cadillac,52045)); + ct.insert(car_model("Deville",cadillac,45195)); + ct.insert(car_model("Seville",cadillac,46330)); + + ct.insert(car_model("ZX2",ford,15355)); + ct.insert(car_model("Thunderbird",ford,43995)); + ct.insert(car_model("Windstar",ford,35510)); + ct.insert(car_model("Focus",ford,19630)); + ct.insert(car_model("Taurus",ford,24290)); + ct.insert(car_model("Mustang",ford,39900)); + ct.insert(car_model("Crown Victoria",ford,30370)); + + ct.insert(car_model("325i",bmw,27800)); + ct.insert(car_model("545i",bmw,54300)); + ct.insert(car_model("745i",bmw,68500)); + ct.insert(car_model("M3 coupe",bmw,46500)); + ct.insert(car_model("Z4 roadster 3.0i",bmw,40250)); + ct.insert(car_model("X5 4.4i",bmw,49950)); + + ct.insert(car_model("A4 1.8T",audi,25940)); + ct.insert(car_model("TT Coupe",audi,33940)); + ct.insert(car_model("A6 3.0",audi,36640)); + ct.insert(car_model("Allroad quattro 2.7T",audi,40640)); + ct.insert(car_model("A8 L",audi,69190)); + + std::cout<<"enter a car manufacturer"<>min_price; + std::cout<<"enter a maximum price"<>max_price; + + { + /* method 1 */ + + /* find all the cars for the manufacturer given */ + + index_iterator::type ic0,ic1; + boost::tuples::tie(ic0,ic1)=get(ct).equal_range(cm); + + /* construct a view (indexed by price) with these */ + + car_table_price_view ctpv; + while(ic0!=ic1){ + ctpv.insert(&*ic0); + ++ic0; + } + + /* select the cars in the range given */ + + car_table_price_view::iterator ictpv0=ctpv.lower_bound(min_price); + car_table_price_view::iterator ictpv1=ctpv.upper_bound(max_price); + if(ictpv0==ictpv1){ + std::cout<<"no cars in the range given"<::type ic0,ic1; + ic0=get(ct).lower_bound(min_price); + ic1=get(ct).upper_bound(max_price); + + /* construct a view with these */ + + car_table_manufacturer_view ctmv; + while(ic0!=ic1){ + ctmv.insert(&*ic0); + ++ic0; + } + + /* select the cars with given manufacturer */ + + car_table_manufacturer_view::iterator ictmv0,ictmv1; + boost::tuples::tie(ictmv0,ictmv1)=ctmv.equal_range(cm); + if(ictmv0==ictmv1){ + std::cout<<"no cars in the range given"< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::multi_index; + +/* A file record maintains some info on name and size as well + * as a pointer to the directory it belongs (null meaning the root + * directory.) + */ + +struct file_entry +{ + file_entry( + std::string name_,unsigned size_,bool is_dir_,const file_entry* dir_): + name(name_),size(size_),is_dir(is_dir_),dir(dir_) + {} + + std::string name; + unsigned size; + bool is_dir; + const file_entry* dir; + + friend std::ostream& operator<<(std::ostream& os,const file_entry& f) + { + os<"; + return os; + } +}; + +/* A file system is just a multi_index_container of entries with indices on + * file and size. These indices are firstly ordered by directory, as commands + * work on a current directory basis. Composite keys are just fine to model + * this. + * NB: We use derivation here instead of simple typedef's as MSVC++ 6.0 + * chokes otherwise. Seems to be related with the complexity of the type + * generated. + */ + +struct name_key:composite_key< + file_entry, + BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry*,dir), + BOOST_MULTI_INDEX_MEMBER(file_entry,std::string,name) +>{}; + +struct size_key:composite_key< + file_entry, + BOOST_MULTI_INDEX_MEMBER(file_entry,const file_entry* const,dir), + BOOST_MULTI_INDEX_MEMBER(file_entry,unsigned,size) +>{}; + +/* see Advanced topics: composite_key in compilers without partial template + * specialization, for info on composite_key_result_less + */ + +typedef multi_index_container< + file_entry, + indexed_by< + /* primary index sorted by name (inside the same directory) */ + ordered_unique< + name_key +#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + ,composite_key_result_less +#endif + >, + /* secondary index sorted by size (inside the same directory) */ + ordered_non_unique< + size_key +#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + ,composite_key_result_less +#endif + > + > +> file_system; + +/* typedef's of the two indices of file_system */ + +typedef nth_index::type file_system_by_name; +typedef nth_index::type file_system_by_size; + +/* We build a rudimentary file system simulation out of some global + * info and a map of commands provided to the user. + */ + +static file_system fs; /* the one and only file system */ +static file_system_by_name& fs_by_name=fs; /* name index to fs */ +static file_system_by_size& fs_by_size=get<1>(fs); /* size index to fs */ +static const file_entry* current_dir=0; /* root directory */ + +/* command framework */ + +/* A command provides an execute memfun fed with the corresponding params + * (first param is stripped off as it serves to identify the command + * currently being used.) + */ + +typedef boost::tokenizer > command_tokenizer; + +class command +{ +public: + virtual ~command(){} + virtual void execute( + command_tokenizer::iterator tok1,command_tokenizer::iterator tok2)=0; +}; + +/* available commands */ + +/* cd: syntax cd [.|..|] */ + +class command_cd:public command +{ +public: + virtual void execute( + command_tokenizer::iterator tok1,command_tokenizer::iterator tok2) + { + if(tok1==tok2)return; + std::string dir=*tok1++; + + if(dir==".")return; + if(dir==".."){ + if(current_dir)current_dir=current_dir->dir; + return; + } + + file_system_by_name::iterator it=fs.find( + boost::make_tuple(current_dir,dir)); + if(it==fs.end()){ + std::cout<<"non-existent directory"<is_dir){ + std::cout<(std::cout,"\n")); + + return; + } + + /* list by name */ + + file_system_by_name::iterator it0,it1; + boost::tie(it0,it1)=fs.equal_range(boost::make_tuple(current_dir)); + std::copy(it0,it1,std::ostream_iterator(std::cout,"\n")); + } +}; +static command_ls ls; + +/* mkdir: syntax mkdir */ + +class command_mkdir:public command +{ +public: + virtual void execute( + command_tokenizer::iterator tok1,command_tokenizer::iterator tok2) + { + std::string dir; + if(tok1!=tok2)dir=*tok1++; + + if(dir.empty()){ + std::cout<<"missing parameter"< command_table; +static command_table cmt; + +int main() +{ + /* fill the file system with some data */ + + file_system::iterator it0,it1; + + fs.insert(file_entry("usr.cfg",240,false,0)); + fs.insert(file_entry("memo.txt",2430,false,0)); + it0=fs.insert(file_entry("dev",0,true,0)).first; + fs.insert(file_entry("tty0",128,false,&*it0)); + fs.insert(file_entry("tty1",128,false,&*it0)); + it0=fs.insert(file_entry("usr",0,true,0)).first; + it1=fs.insert(file_entry("bin",0,true,&*it0)).first; + fs.insert(file_entry("bjam",172032,false,&*it1)); + it0=fs.insert(file_entry("home",0,true,0)).first; + it1=fs.insert(file_entry("andy",0,true,&*it0)).first; + fs.insert(file_entry("logo.jpg",5345,false,&*it1)).first; + fs.insert(file_entry("foo.cpp",890,false,&*it1)).first; + fs.insert(file_entry("foo.hpp",93,false,&*it1)).first; + fs.insert(file_entry("foo.html",750,false,&*it1)).first; + fs.insert(file_entry("a.obj",12302,false,&*it1)).first; + fs.insert(file_entry(".bash_history",8780,false,&*it1)).first; + it1=fs.insert(file_entry("rachel",0,true,&*it0)).first; + fs.insert(file_entry("test.py",650,false,&*it1)).first; + fs.insert(file_entry("todo.txt",241,false,&*it1)).first; + fs.insert(file_entry(".bash_history",9510,false,&*it1)).first; + + /* fill the command table */ + + cmt["cd"] =&cd; + cmt["ls"] =&ls; + cmt["mkdir"]=&mkdir; + + /* main looop */ + + for(;;){ + /* print out the current directory and the prompt symbol */ + + if(current_dir)std::cout<name; + std::cout<<">"; + + /* get an input line from the user: if empty, exit the program */ + + std::string com; + std::getline(std::cin,com); + command_tokenizer tok(com,boost::char_separator(" \t\n")); + if(tok.begin()==tok.end())break; /* null command, exit */ + + /* select the corresponding command and execute it */ + + command_table::iterator it=cmt.find(*tok.begin()); + if(it==cmt.end()){ + std::cout<<"invalid command"<second->execute(++tok.begin(),tok.end()); + } + + return 0; +} diff --git a/example/memfun_key.cpp b/example/memfun_key.cpp new file mode 100644 index 0000000..c5183cc --- /dev/null +++ b/example/memfun_key.cpp @@ -0,0 +1,76 @@ +/* Boost.MultiIndex example of member functions used as key extractors. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#if !defined(NDEBUG) +#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING +#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE +#endif + +#include +#include +#include +#include +#include + +using namespace boost::multi_index; + +/* A name record consists of the given name (e.g. "Charlie") + * and the family name (e.g. "Brown"). The full name, calculated + * by name_record::name() is laid out in the "phonebook order" + * family name + given_name. + */ + +struct name_record +{ + name_record(std::string given_name,std::string family_name): + given_name(given_name),family_name(family_name) + {} + + std::string name()const + { + std::string str=family_name; + str+=" "; + str+=given_name; + return str; + } + +private: + std::string given_name; + std::string family_name; +}; + +/* multi_index_container with only one index based on name_record::name() */ + +typedef multi_index_container< + name_record, + indexed_by< + ordered_unique< + BOOST_MULTI_INDEX_CONST_MEM_FUN(name_record,std::string,name) + > + > +> name_record_set; + +int main() +{ + name_record_set ns; + + ns.insert(name_record("Joe","Smith")); + ns.insert(name_record("Robert","Nightingale")); + ns.insert(name_record("Robert","Brown")); + ns.insert(name_record("Marc","Tuxedo")); + + /* list the names in ns in phonebook order */ + + for(name_record_set::iterator it=ns.begin();it!=ns.end();++it){ + std::cout<name()< +#include +#include +#include +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; + +/* modulo_less order numbers according to their division residual. + * For instance, if modulo==10 then 22 is less than 15 as 22%10==2 and + * 15%10==5. + */ + +template +struct modulo_less +{ + modulo_less(IntegralType m):modulo(m){} + + bool operator()(IntegralType x,IntegralType y)const + { + return (x%modulo)<(y%modulo); + } + +private: + IntegralType modulo; +}; + +/* multi_index_container of unsigned ints holding a "natural" index plus + * an ordering based on modulo_less. + */ + +typedef multi_index_container< + unsigned int, + indexed_by< + ordered_unique >, + ordered_non_unique, modulo_less > + > +> modulo_indexed_set; + +int main() +{ + /* define a modulo_indexed_set with modulo==10 */ + + modulo_indexed_set::ctor_args_list args_list= + boost::make_tuple( + /* ctor_args for index #0 is default constructible */ + nth_index::type::ctor_args(), + + /* first parm is key_from_value, second is our sought for key_compare */ + boost::make_tuple(identity(),modulo_less(10)) + ); + + modulo_indexed_set m(args_list); + /* this could have be written online without the args_list variable, + * left as it is for explanatory purposes. */ + + /* insert some numbers */ + + unsigned int numbers[]={0,1,20,40,33,68,11,101,60,34,88,230,21,4,7,17}; + const std::size_t numbers_length(sizeof(numbers)/sizeof(numbers[0])); + + m.insert(&numbers[0],&numbers[numbers_length]); + + /* lists all numbers in order, along with their "equivalence class", that is, + * the equivalent numbers under modulo_less + */ + + for(modulo_indexed_set::iterator it=m.begin();it!=m.end();++it){ + std::cout<<*it<<" -> ( "; + + nth_index::type::iterator it0,it1; + boost::tie(it0,it1)=get<1>(m).equal_range(*it); + std::copy(it0,it1,std::ostream_iterator(std::cout," ")); + + std::cout<<")"< +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using boost::multi_index_container; +using namespace boost::multi_index; + +/* text_container holds words as inserted and also keep them indexed + * by dictionary order. + */ + +typedef multi_index_container< + std::string, + indexed_by< + sequenced<>, + ordered_non_unique > + > +> text_container; + +/* ordered index */ + +typedef nth_index::type ordered_text; + +typedef boost::tokenizer > text_tokenizer; + +int main() +{ + std::string text= + "Alice was beginning to get very tired of sitting by her sister on the " + "bank, and of having nothing to do: once or twice she had peeped into the " + "book her sister was reading, but it had no pictures or conversations in " + "it, 'and what is the use of a book,' thought Alice 'without pictures or " + "conversation?'"; + + /* feed the text into the container */ + + text_container tc; + text_tokenizer tok(text,boost::char_separator(" \t\n.,;:!?'\"-")); + std::copy(tok.begin(),tok.end(),std::back_inserter(tc)); + + /* list all words in alphabetical order along with their number + * of occurrences + */ + + ordered_text& ot=get<1>(tc); + for(ordered_text::iterator it=ot.begin();it!=ot.end();){ + std::cout<(std::cout," ")); + std::cout<(std::cout," ")); + std::cout< /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* A composite key stores n key extractors and "computes" the + * result on a given value as a packed reference to the value and + * the composite key itself. Actual invocations to the component + * key extractors are lazily performed on comparison time. + * As the other key extractors in Boost.MultiIndex, composite_key + * is overloaded to work on chained pointers to T and reference_wrappers + * of T. + * composite_key_compare is overloaded to enable comparisons between + * composite_key_results and tuples of values. Comparison is done + * lexicographically on on the maximum common number of elements of the + * operands. This allows searching for incomplete keys + */ + +/* This user_definable macro limits the number of elements of a composite + * key; useful for shortening resulting symbol names (MSVC++ 6.0, for + * instance has problems coping with very long symbol names.) + * NB: This cannot exceed the maximum number of arguments of + * boost::tuple. In Boost 1.31, the limit is 10. + */ + +#if !defined(BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE) +#if defined(BOOST_MSVC)&&(BOOST_MSVC<1300) +#define BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE 5 +#else +#define BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE 10 +#endif +#endif + +/* maximum number of key extractors in a composite key */ + +#if BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE<10 /* max length of a tuple */ +#define BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE \ + BOOST_MULTI_INDEX_LIMIT_COMPOSITE_KEY_SIZE +#else +#define BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE 10 +#endif + +/* BOOST_PP_ENUM of BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE elements */ + +#define BOOST_MULTI_INDEX_CK_ENUM(macro,data) \ + BOOST_PP_ENUM(BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE,macro,data) + +/* BOOST_PP_ENUM_PARAMS of BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE elements */ + +#define BOOST_MULTI_INDEX_CK_ENUM_PARAMS(param) \ + BOOST_PP_ENUM_PARAMS(BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE,param) + +/* if n==0 -> text0 + * otherwise -> textn=tuples::null_type + */ + +#define BOOST_MULTI_INDEX_CK_TEMPLATE_PARM(z,n,text) \ + typename BOOST_PP_CAT(text,n) BOOST_PP_EXPR_IF(n,=tuples::null_type) + +/* const textn& kn=textn() */ + +#define BOOST_MULTI_INDEX_CK_CTOR_ARG(z,n,text) \ + const BOOST_PP_CAT(text,n)& BOOST_PP_CAT(k,n) = BOOST_PP_CAT(text,n)() + +/* typename list(0)::type */ + +#define BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N(z,n,list) \ + BOOST_DEDUCED_TYPENAME BOOST_PP_LIST_AT(list,0)< \ + BOOST_PP_LIST_AT(list,1),n \ + >::type + +namespace boost{ + +template class reference_wrapper; /* fwd decl. */ + +namespace multi_index{ + +namespace detail{ + +/* nth_composite_key_less:: yields std::less + * where result_type is the result_type of the nth key extractor of + * CompositeKey. If N >= the length of CompositeKey, it yields + * tuples::null_type. + * Similar thing for nth_composite_key_greater. + */ + +template +struct nth_key_from_value +{ + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename prevent_eti< + tuples::element, + typename mpl::apply_if_c< + N::value, + tuples::element, + mpl::identity + >::type + >::type type; +}; + +template +struct key_std_less +{ + typedef std::less type; +}; + +template<> +struct key_std_less +{ + typedef tuples::null_type type; +}; + +template +struct nth_composite_key_less +{ + typedef typename nth_key_from_value::type key_from_value; + typedef typename key_std_less::type type; +}; + +template +struct key_std_greater +{ + typedef std::greater type; +}; + +template<> +struct key_std_greater +{ + typedef tuples::null_type type; +}; + +template +struct nth_composite_key_greater +{ + typedef typename nth_key_from_value::type key_from_value; + typedef typename key_std_greater::type type; +}; + +/* Metaprogramming machinery to compare composite_key_results between + * them and with tuples of values. + * equals_* computes equality of two tuple objects x,y with the same + * length, defined as + * + * xi==yi for all i in [0,...,min(length(x),length(y))). + * + * less_* accepts operands of different lenghts and computes the + * following less-than relation: + * + * !(xi +struct equals_ckey_ckey; /* fwd decl. */ + +template +struct equals_ckey_ckey_terminal +{ + static bool compare( + const KeyCons1&,const Value1&, + const KeyCons2&,const Value2&) + { + return true; + } +}; + +template +struct equals_ckey_ckey_normal +{ + static bool compare( + const KeyCons1& c0,const Value1& v0, + const KeyCons2& c1,const Value2& v1) + { + if(!(c0.get_head()(v0)==c1.get_head()(v1)))return false; + return equals_ckey_ckey< + BOOST_DEDUCED_TYPENAME KeyCons1::tail_type,Value1, + BOOST_DEDUCED_TYPENAME KeyCons2::tail_type,Value2 + >::compare(c0.get_tail(),v0,c1.get_tail(),v1); + } +}; + +template +struct equals_ckey_ckey: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + equals_ckey_ckey_terminal, + equals_ckey_ckey_normal + >::type +{ +}; + +template +struct equals_ckey_cval; /* fwd decl. */ + +template +struct equals_ckey_cval_terminal +{ + static bool compare(const KeyCons&,const Value&,const ValCons&) + { + return true; + } + + static bool compare(const ValCons&,const KeyCons&,const Value&) + { + return true; + } +}; + +template +struct equals_ckey_cval_normal +{ + static bool compare(const KeyCons& c,const Value& v,const ValCons& vc) + { + if(!(c.get_head()(v)==vc.get_head()))return false; + return equals_ckey_cval< + BOOST_DEDUCED_TYPENAME KeyCons::tail_type,Value, + BOOST_DEDUCED_TYPENAME ValCons::tail_type + >::compare(c.get_tail(),v,vc.get_tail()); + } + + static bool compare(const ValCons& vc,const KeyCons& c,const Value& v) + { + if(!(vc.get_head()==c.get_head()(v)))return false; + return equals_ckey_cval< + BOOST_DEDUCED_TYPENAME KeyCons::tail_type,Value, + BOOST_DEDUCED_TYPENAME ValCons::tail_type + >::compare(vc.get_tail(),c.get_tail(),v); + } +}; + +template +struct equals_ckey_cval: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + equals_ckey_cval_terminal, + equals_ckey_cval_normal + >::type +{ +}; + +template +struct less_ckey_ckey; /* fwd decl. */ + +template +struct less_ckey_ckey_terminal +{ + static bool compare( + const KeyCons1&,const Value1&,const KeyCons2&,const Value2&) + { + return false; + } +}; + +template +struct less_ckey_ckey_normal +{ + static bool compare( + const KeyCons1& c0,const Value1& v0, + const KeyCons2& c1,const Value2& v1) + { + if(c0.get_head()(v0)::compare(c0.get_tail(),v0,c1.get_tail(),v1); + } +}; + +template +struct less_ckey_ckey: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + less_ckey_ckey_terminal, + less_ckey_ckey_normal + >::type +{ +}; + +template +struct less_ckey_cval; /* fwd decl. */ + +template +struct less_ckey_cval_terminal +{ + static bool compare(const KeyCons&,const Value&,const ValCons&) + { + return false; + } + + static bool compare(const ValCons&,const KeyCons&,const Value&) + { + return false; + } +}; + +template +struct less_ckey_cval_normal +{ + static bool compare(const KeyCons& c,const Value& v,const ValCons& vc) + { + if(c.get_head()(v)::compare(c.get_tail(),v,vc.get_tail()); + } + + static bool compare(const ValCons& vc,const KeyCons& c,const Value& v) + { + if(vc.get_head()::compare(vc.get_tail(),c.get_tail(),v); + } +}; + +template +struct less_ckey_cval: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + less_ckey_cval_terminal, + less_ckey_cval_normal + >::type +{ +}; + +template +< + typename KeyCons1,typename Value1, + typename KeyCons2, typename Value2, + typename CompareCons +> +struct compare_ckey_ckey; /* fwd decl. */ + +template +< + typename KeyCons1,typename Value1, + typename KeyCons2, typename Value2, + typename CompareCons +> +struct compare_ckey_ckey_terminal +{ + static bool compare( + const KeyCons1&,const Value1&, + const KeyCons2&,const Value2&, + const CompareCons&) + { + return false; + } +}; + +template +< + typename KeyCons1,typename Value1, + typename KeyCons2, typename Value2, + typename CompareCons +> +struct compare_ckey_ckey_normal +{ + static bool compare( + const KeyCons1& c0,const Value1& v0, + const KeyCons2& c1,const Value2& v1, + const CompareCons& comp) + { + if(comp.get_head()(c0.get_head()(v0),c1.get_head()(v1)))return true; + if(comp.get_head()(c1.get_head()(v1),c0.get_head()(v0)))return false; + return compare_ckey_ckey< + BOOST_DEDUCED_TYPENAME KeyCons1::tail_type,Value1, + BOOST_DEDUCED_TYPENAME KeyCons2::tail_type,Value2, + BOOST_DEDUCED_TYPENAME CompareCons::tail_type + >::compare(c0.get_tail(),v0,c1.get_tail(),v1,comp.get_tail()); + } +}; + +template +< + typename KeyCons1,typename Value1, + typename KeyCons2, typename Value2, + typename CompareCons +> +struct compare_ckey_ckey: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + compare_ckey_ckey_terminal, + compare_ckey_ckey_normal + >::type +{ +}; + +template +< + typename KeyCons,typename Value, + typename ValCons,typename CompareCons +> +struct compare_ckey_cval; /* fwd decl. */ + +template +< + typename KeyCons,typename Value, + typename ValCons,typename CompareCons +> +struct compare_ckey_cval_terminal +{ + static bool compare( + const KeyCons&,const Value&,const ValCons&,const CompareCons&) + { + return false; + } + + static bool compare( + const ValCons&,const KeyCons&,const Value&,const CompareCons&) + { + return false; + } +}; + +template +< + typename KeyCons,typename Value, + typename ValCons,typename CompareCons +> +struct compare_ckey_cval_normal +{ + static bool compare( + const KeyCons& c,const Value& v,const ValCons& vc, + const CompareCons& comp) + { + if(comp.get_head()(c.get_head()(v),vc.get_head()))return true; + if(comp.get_head()(vc.get_head(),c.get_head()(v)))return false; + return compare_ckey_cval< + BOOST_DEDUCED_TYPENAME KeyCons::tail_type,Value, + BOOST_DEDUCED_TYPENAME ValCons::tail_type, + BOOST_DEDUCED_TYPENAME CompareCons::tail_type + >::compare(c.get_tail(),v,vc.get_tail(),comp.get_tail()); + } + + static bool compare( + const ValCons& vc,const KeyCons& c,const Value& v, + const CompareCons& comp) + { + if(comp.get_head()(vc.get_head(),c.get_head()(v)))return true; + if(comp.get_head()(c.get_head()(v),vc.get_head()))return false; + return compare_ckey_cval< + BOOST_DEDUCED_TYPENAME KeyCons::tail_type,Value, + BOOST_DEDUCED_TYPENAME ValCons::tail_type, + BOOST_DEDUCED_TYPENAME CompareCons::tail_type + >::compare(vc.get_tail(),c.get_tail(),v,comp.get_tail()); + } +}; + +template +< + typename KeyCons,typename Value, + typename ValCons,typename CompareCons +> +struct compare_ckey_cval: + mpl::if_< + mpl::or_< + is_same, + is_same + >, + compare_ckey_cval_terminal, + compare_ckey_cval_normal + >::type +{ +}; + +} /* namespace multi_index::detail */ + +/* composite_key_result */ + +template +struct composite_key_result +{ + typedef CompositeKey composite_key_type; + typedef typename composite_key_type::value_type value_type; + + composite_key_result( + const composite_key_type& composite_key_,const value_type& value_): + composite_key(composite_key_),value(value_) + {} + + const composite_key_type& composite_key; + const value_type& value; +}; + +/* composite_key */ + +template< + typename Value, + BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,KeyFromValue) +> +struct composite_key: + private tuple +{ +private: + typedef tuple super; + +public: + typedef super key_extractor_tuple; + typedef Value value_type; + typedef composite_key_result result_type; + + composite_key( + BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,KeyFromValue)): + super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) + {} + + composite_key(const key_extractor_tuple& x):super(x){} + + const key_extractor_tuple& key_extractors()const{return *this;} + key_extractor_tuple& key_extractors(){return *this;} + + template + result_type operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + result_type operator()(const value_type& x)const + { + return result_type(*this,x); + } + + result_type operator()(const reference_wrapper& x)const + { + return result_type(*this,x.get()); + } + + result_type operator()(const reference_wrapper& x)const + { + return result_type(*this,x.get()); + } +}; + +/* comparison operators */ + +/* == */ + +template +inline bool operator==( + const composite_key_result& x, + const composite_key_result& y) +{ + typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; + typedef typename CompositeKey1::value_type value_type1; + typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; + typedef typename CompositeKey2::value_type value_type2; + + BOOST_STATIC_ASSERT( + tuples::length::value== + tuples::length::value); + + return detail::equals_ckey_ckey< + key_extractor_tuple1,value_type1, + key_extractor_tuple2,value_type2 + >::compare( + x.composite_key.key_extractors(),x.value, + y.composite_key.key_extractors(),y.value); +} + +template< + typename CompositeKey, + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value) +> +inline bool operator==( + const composite_key_result& x, + const tuple& y) +{ + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + BOOST_STATIC_ASSERT( + tuples::length::value== + tuples::length::value); + + return detail::equals_ckey_cval:: + compare(x.composite_key.key_extractors(),x.value,y); +} + +template +< + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), + typename CompositeKey +> +inline bool operator==( + const tuple& x, + const composite_key_result& y) +{ + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + BOOST_STATIC_ASSERT( + tuples::length::value== + tuples::length::value); + + return detail::equals_ckey_cval:: + compare(x,y.composite_key.key_extractors(),y.value); +} + +/* < */ + +template +inline bool operator<( + const composite_key_result& x, + const composite_key_result& y) +{ + typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; + typedef typename CompositeKey1::value_type value_type1; + typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; + typedef typename CompositeKey2::value_type value_type2; + + return detail::less_ckey_ckey< + key_extractor_tuple1,value_type1, + key_extractor_tuple2,value_type2 + >::compare( + x.composite_key.key_extractors(),x.value, + y.composite_key.key_extractors(),y.value); +} + +template +< + typename CompositeKey, + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value) +> +inline bool operator<( + const composite_key_result& x, + const tuple& y) +{ + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + return detail::less_ckey_cval:: + compare(x.composite_key.key_extractors(),x.value,y); +} + +template +< + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), + typename CompositeKey +> +inline bool operator<( + const tuple& x, + const composite_key_result& y) +{ + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + return detail::less_ckey_cval:: + compare(x,y.composite_key.key_extractors(),y.value); +} + +/* rest of comparison operators */ + +#define BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS(t1,t2,a1,a2) \ +template inline bool operator!=(const a1& x,const a2& y) \ +{ \ + return !(x==y); \ +} \ + \ +template inline bool operator>(const a1& x,const a2& y) \ +{ \ + return y inline bool operator>=(const a1& x,const a2& y) \ +{ \ + return !(x inline bool operator<=(const a1& x,const a2& y) \ +{ \ + return !(y, + composite_key_result +) + +BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( + typename CompositeKey, + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), + composite_key_result, + tuple +) + +BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS( + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), + typename CompositeKey, + tuple, + composite_key_result +) + +/* composite_key_compare */ + +template +< + BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_TEMPLATE_PARM,Compare) +> +struct composite_key_compare: + private tuple +{ +private: + typedef tuple super; + +public: + typedef super key_comp_tuple; + + composite_key_compare( + BOOST_MULTI_INDEX_CK_ENUM(BOOST_MULTI_INDEX_CK_CTOR_ARG,Compare)): + super(BOOST_MULTI_INDEX_CK_ENUM_PARAMS(k)) + {} + + composite_key_compare(const key_comp_tuple& x):super(x){} + + const key_comp_tuple& key_comps()const{return *this;} + key_comp_tuple& key_comps(){return *this;} + + template + bool operator()( + const composite_key_result & x, + const composite_key_result & y)const + { + typedef typename CompositeKey1::key_extractor_tuple key_extractor_tuple1; + typedef typename CompositeKey1::value_type value_type1; + typedef typename CompositeKey2::key_extractor_tuple key_extractor_tuple2; + typedef typename CompositeKey2::value_type value_type2; + + BOOST_STATIC_ASSERT( + tuples::length::value<= + tuples::length::value|| + tuples::length::value<= + tuples::length::value); + + return detail::compare_ckey_ckey< + key_extractor_tuple1,value_type1, + key_extractor_tuple2,value_type2, + key_comp_tuple + >::compare( + x.composite_key.key_extractors(),x.value, + y.composite_key.key_extractors(),y.value, + key_comps()); + } + + template + < + typename CompositeKey, + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value) + > + bool operator()( + const composite_key_result& x, + const tuple& y)const + { + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + BOOST_STATIC_ASSERT( + tuples::length::value<= + tuples::length::value|| + tuples::length::value<= + tuples::length::value); + + return detail::compare_ckey_cval< + key_extractor_tuple,value_type, + key_tuple,key_comp_tuple + >::compare(x.composite_key.key_extractors(),x.value,y,key_comps()); + } + + template + < + BOOST_MULTI_INDEX_CK_ENUM_PARAMS(typename Value), + typename CompositeKey + > + bool operator()( + const tuple& x, + const composite_key_result& y)const + { + typedef typename CompositeKey::key_extractor_tuple key_extractor_tuple; + typedef typename CompositeKey::value_type value_type; + typedef tuple key_tuple; + + BOOST_STATIC_ASSERT( + tuples::length::value<= + tuples::length::value|| + tuples::length::value<= + tuples::length::value); + + return detail::compare_ckey_cval< + key_extractor_tuple,value_type, + key_tuple,key_comp_tuple + >::compare(x,y.composite_key.key_extractors(),y.value,key_comps()); + } +}; + +/* composite_key_compare_less is merely a composite_key_compare + * instantiation with the corresponding std::less<> comparison + * predicates for each key extractor. Useful as a substitute for + * std::less when the compiler does not + * support partial specialization. + * Same with composite_key_compare_greater. + */ + +#define BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER \ +composite_key_compare< \ + BOOST_MULTI_INDEX_CK_ENUM( \ + BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ + /* the argument is a PP list */ \ + (detail::nth_composite_key_less, \ + (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ + BOOST_PP_NIL))) \ + > + +template +struct composite_key_result_less: +BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS +BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER +{ +private: + typedef BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER super; + +public: + typedef CompositeKeyResult first_argument_type; + typedef first_argument_type second_argument_type; + typedef bool result_type; + + using super::operator(); +}; + +#define BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER \ +composite_key_compare< \ + BOOST_MULTI_INDEX_CK_ENUM( \ + BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N, \ + /* the argument is a PP list */ \ + (detail::nth_composite_key_greater, \ + (BOOST_DEDUCED_TYPENAME CompositeKeyResult::composite_key_type, \ + BOOST_PP_NIL))) \ + > + +template +struct composite_key_result_greater: +BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS +BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER +{ +private: + typedef BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER super; + +public: + typedef CompositeKeyResult first_argument_type; + typedef first_argument_type second_argument_type; + typedef bool result_type; + + using super::operator(); +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +/* Specialization of std::less and std::greater for composite_key_results + * enabling comparison with tuples of values. + */ + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) +namespace std{ + +template +struct less >: + boost::multi_index::composite_key_result_less< + boost::multi_index::composite_key_result + > +{ +}; + +template +struct greater >: + boost::multi_index::composite_key_result_greater< + boost::multi_index::composite_key_result + > +{ +}; + +} /* namespace std */ +#endif + +#undef BOOST_MULTI_INDEX_CK_RESULT_LESS_SUPER +#undef BOOST_MULTI_INDEX_CK_RESULT_GREATER_SUPER +#undef BOOST_MULTI_INDEX_CK_COMPLETE_COMP_OPS +#undef BOOST_MULTI_INDEX_CK_APPLY_METAFUNCTION_N +#undef BOOST_MULTI_INDEX_CK_CTOR_ARG +#undef BOOST_MULTI_INDEX_CK_TEMPLATE_PARM +#undef BOOST_MULTI_INDEX_CK_ENUM_PARAMS +#undef BOOST_MULTI_INDEX_CK_ENUM +#undef BOOST_MULTI_INDEX_COMPOSITE_KEY_SIZE + +#endif diff --git a/include/boost/multi_index/detail/access_specifier.hpp b/include/boost/multi_index/detail/access_specifier.hpp new file mode 100644 index 0000000..5d367b0 --- /dev/null +++ b/include/boost/multi_index/detail/access_specifier.hpp @@ -0,0 +1,39 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ACCESS_SPECIFIER_HPP +#define BOOST_MULTI_INDEX_DETAIL_ACCESS_SPECIFIER_HPP + +#include +#include + +/* In those compilers that do not accept the member template friend syntax, + * some protected and private sections might need to be specified as + * public. + */ + +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) +#define BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS protected +#define BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS private +#else +#define BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS public +#define BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS public +#endif + +/* GCC does not correctly support in-class using declarations for template + * functions. See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=9810 + */ + +#if BOOST_WORKAROUND(__GNUC__, <3)||\ + BOOST_WORKAROUND(__GNUC__,==3)&&(__GNUC_MINOR__<4) +#define BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS public +#else +#define BOOST_MULTI_INDEX_PRIVATE_IF_USING_DECL_FOR_TEMPL_FUNCTIONS private +#endif + +#endif diff --git a/include/boost/multi_index/detail/allocator.hpp b/include/boost/multi_index/detail/allocator.hpp new file mode 100644 index 0000000..63bb911 --- /dev/null +++ b/include/boost/multi_index/detail/allocator.hpp @@ -0,0 +1,213 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ALLOCATOR_HPP +#define BOOST_MULTI_INDEX_DETAIL_ALLOCATOR_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* allocator adaption layer */ + +namespace allocator{ + +/* Detects whether a given allocator is from old Dinkumware's stdlib, + * which lacks rebind and uses an ugly _Charalloc memfun as a poor + * replacement. + * Note that it does not suffice to check the Boost.Config stdlib + * macros, as the user might have passed a custom, compliant allocator. + */ + +#if defined(BOOST_DINKUMWARE_STDLIB)&&defined(BOOST_NO_STD_ALLOCATOR) + +template +struct dinkumware_defective_allocator +{ + BOOST_STATIC_CONSTANT(bool, + value=( + is_same< + Allocator, + std::allocator + >::value)); +}; + +#else + +template +struct dinkumware_defective_allocator +{ + BOOST_STATIC_CONSTANT(bool,value=false); +}; + +#endif + +/* allocator ops for Dinkumware's defective allocator */ + +template +struct dinkumware_defective_allocator_ops +{ + typedef Allocator rebound_type; + + static Type* allocate(Allocator& al,typename Allocator::size_type n) + { + return static_cast( + static_cast(al._Charalloc(n*sizeof(Type)))); + } + + static void deallocate(Allocator& al,Type* p,typename Allocator::size_type n) + { + al.deallocate(p,n); + } + + static void construct(Allocator&,Type* p,const Type& t) + { + new(static_cast(p))Type(t); + } + + static void destroy(Allocator&,Type* p) + { + (static_cast(p))->~Type(); + } +}; + +/* allocator ops for compliant allocators */ + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +/* Workaround for a problem in MSVC with dependent template typedefs + * when doing rebinding of allocators. + * Modeled after (thanks, Aleksey!) + */ + +template +struct msvc_rebind +{ + template struct fake_allocator:Allocator{}; + template<> struct fake_allocator + { + template struct rebind{}; + }; + + template + struct result: + fake_allocator::value>:: + template rebind + { + }; +}; +#endif + +template +struct compliant_allocator_ops +{ + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef typename msvc_rebind:: + template result::other rebound_type; +#else + typedef typename Allocator:: + BOOST_NESTED_TEMPLATE rebind::other rebound_type; +#endif + + static Type* allocate(Allocator& al,typename Allocator::size_type n) + { + return al.allocate(n); + } + + static void deallocate(Allocator& al,Type* p,typename Allocator::size_type n) + { + al.deallocate(p,n); + } + + static void construct(Allocator& al,Type *p,const Type& t) + { + al.construct(p,t); + } + + static void destroy(Allocator& al,Type* p) + { + al.destroy(p); + } +}; + +template +struct allocator_ops: + mpl::if_c< + dinkumware_defective_allocator::value, + dinkumware_defective_allocator_ops, + compliant_allocator_ops + >::type +{ +}; + +/* allocator ops front end */ + +template +struct rebind_to +{ + typedef typename allocator_ops::rebound_type type; +}; + +template +Type* allocate( + Allocator& al,typename Allocator::size_type n + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Type)) +{ + return allocator_ops::allocate(al,n); +} + +template +void deallocate(Allocator& al,Type* p,typename Allocator::size_type n) +{ + allocator_ops::deallocate(al,p,n); +} + +template +void construct(Allocator& al,Type* p,const Type& t) +{ + allocator_ops::construct(al,p,t); +} + +template +void destroy(Allocator& al,Type* p) +{ + allocator_ops::destroy(al,p); +} + +/* allocator-independent versions of construct and destroy */ + +template +void construct(void* p,const Type& t) +{ + new (p) Type(t); +} + +template +void destroy(const Type* p) +{ + p->~Type(); +} + +} /* namespace multi_index::detail::allocator */ + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/auto_space.hpp b/include/boost/multi_index/detail/auto_space.hpp new file mode 100644 index 0000000..d5d6d8e --- /dev/null +++ b/include/boost/multi_index/detail/auto_space.hpp @@ -0,0 +1,64 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_AUTO_SPACE_HPP +#define BOOST_MULTI_INDEX_DETAIL_AUTO_SPACE_HPP + +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* auto_space provides uninitialized space suitably to store + * a given number of elements of a given type. + */ + +/* NB: it is not clear whether using an allocator to handle + * zero-sized arrays of elements is conformant or not. GCC 3.3.1 + * and prior fail here, other stdlibs handle the issue gracefully. + * To be on the safe side, the case n==0 is given special treatment. + * References: + * GCC Bugzilla, "standard allocator crashes when deallocating segment + * "of zero length", http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14176 + * C++ Standard Library Defect Report List (Revision 28), issue 199 + * "What does allocate(0) return?", + * http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#199 + */ + +template > +struct auto_space:private noncopyable +{ + explicit auto_space(const Allocator& al=Allocator(),std::size_t n=1): + al_(al),n_(n),data_(n_?allocator::allocate(al_,n_):0) + {} + + ~auto_space() + { + if(n_)allocator::deallocate(al_,data_,n_); + } + + T* data()const{return data_;} + +private: + typename allocator::rebind_to::type al_; + std::size_t n_; + T* data_; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/base_type.hpp b/include/boost/multi_index/detail/base_type.hpp new file mode 100644 index 0000000..f783d48 --- /dev/null +++ b/include/boost/multi_index/detail/base_type.hpp @@ -0,0 +1,81 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_BASE_TYPE_HPP +#define BOOST_MULTI_INDEX_DETAIL_BASE_TYPE_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* MPL machinery to construct a linear hierarchy of indices out of + * a index list. + */ + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +struct index_applier +{ + template + struct apply: + msvc_index_specifier:: + template result_index_class + { + }; +}; +#else +struct index_applier +{ + template + struct apply:IndexSpecifierIterator::type:: + BOOST_NESTED_TEMPLATE index_class + { + }; +}; +#endif + +template +struct multi_index_base_type +{ + BOOST_STATIC_ASSERT(detail::is_index_list::value); + + typedef typename prevent_eti< + multi_index_container, + typename mpl::iter_fold_backward< + IndexSpecifierList, + index_base, + mpl::bind2< + index_applier, + mpl::_2, + mpl::_1 + > + >::type + >::type type; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/converter.hpp b/include/boost/multi_index/detail/converter.hpp new file mode 100644 index 0000000..fd3e07e --- /dev/null +++ b/include/boost/multi_index/detail/converter.hpp @@ -0,0 +1,48 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_CONVERTER_HPP +#define BOOST_MULTI_INDEX_DETAIL_CONVERTER_HPP + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* converter offers means to access indices of a given multi_index_container + * and for convertibilty between index iterators, so providing a + * localized access point for get() and project() functions. + */ + +template +struct converter +{ + static const Index& index(const MultiIndexContainer& x){return x;} + static Index& index(MultiIndexContainer& x){return x;} + + static typename Index::const_iterator const_iterator( + const MultiIndexContainer& x,typename MultiIndexContainer::node_type* node) + { + return x.Index::make_iterator(node); + } + + static typename Index::iterator iterator( + MultiIndexContainer& x,typename MultiIndexContainer::node_type* node) + { + return x.Index::make_iterator(node); + } +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/copy_map.hpp b/include/boost/multi_index/detail/copy_map.hpp new file mode 100644 index 0000000..496cc86 --- /dev/null +++ b/include/boost/multi_index/detail/copy_map.hpp @@ -0,0 +1,127 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_COPY_MAP_HPP +#define BOOST_MULTI_INDEX_DETAIL_COPY_MAP_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* copy_map is used as an auxiliary structure during copy_() operations. + * When a container with n nodes is replicated, node_map holds the pairings + * between original and copied nodes, and provides a fast way to find a + * copied node from an original one. + * The semantics of the class are not simple, and no attempt has been made + * to enforce it: multi_index_container handles it right. On the other hand, + * the const interface, which is the one provided to index implementations, + * only allows for: + * - Enumeration of pairs of (original,copied) nodes (excluding the headers), + * - fast retrieval of copied nodes (including the headers.) + */ + +template +struct copy_map_entry +{ + copy_map_entry(Node* f,Node* s):first(f),second(s){} + + Node* first; + Node* second; + + bool operator<(const copy_map_entry& x)const + { + return std::less()(first,x.first); + } +}; + +template +class copy_map:private noncopyable +{ +public: + typedef const copy_map_entry* const_iterator; + + copy_map( + const Allocator al,std::size_t size,Node* header_org,Node* header_cpy): + al_(al),size_(size),spc(al_,size_),n(0), + header_org_(header_org),header_cpy_(header_cpy),released(false) + {} + + ~copy_map() + { + if(!released){ + for(std::size_t i=0;ivalue); + deallocate(spc.data()[i].second); + } + } + } + + const_iterator begin()const{return &spc.data()[0];} + const_iterator end()const{return &spc.data()[n];} + + void clone(Node* node) + { + spc.data()[n].first=node; + spc.data()[n].second=allocator::allocate(al_,1); + BOOST_TRY{ + allocator::construct(&spc.data()[n].second->value,node->value); + } + BOOST_CATCH(...){ + deallocate(spc.data()[n].second); + BOOST_RETHROW; + } + BOOST_CATCH_END + ++n; + + if(n==size_)std::sort(&spc.data()[0],&spc.data()[size_]); + } + + Node* find(Node* node)const + { + if(node==header_org_)return header_cpy_; + return std::lower_bound( + &spc.data()[0],&spc.data()[n],copy_map_entry(node,0))->second; + }; + + void release() + { + released=true; + } + +private: + typename allocator::rebind_to::type al_; + std::size_t size_; + auto_space,Allocator> spc; + std::size_t n; + Node* header_org_; + Node* header_cpy_; + bool released; + + void deallocate(Node* node) + { + allocator::deallocate(al_,node,1); + } +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/def_ctor_tuple_cons.hpp b/include/boost/multi_index/detail/def_ctor_tuple_cons.hpp new file mode 100644 index 0000000..02a8cd4 --- /dev/null +++ b/include/boost/multi_index/detail/def_ctor_tuple_cons.hpp @@ -0,0 +1,55 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_DEF_CTOR_TUPLE_CONS_HPP +#define BOOST_MULTI_INDEX_DETAIL_DEF_CTOR_TUPLE_CONS_HPP + +#include + +#if defined(BOOST_MSVC) +/* In MSVC, tuples::cons is not default constructible. We provide a + * tiny wrapper around tuple::cons filling that hole. + */ + +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template +struct default_constructible_tuple_cons:Cons +{ + default_constructible_tuple_cons(): + Cons( + Cons::head_type(), + static_cast( + default_constructible_tuple_cons())) + {} + + default_constructible_tuple_cons(const Cons& cons):Cons(cons){} +}; + +template<> +struct default_constructible_tuple_cons:tuples::null_type +{ + default_constructible_tuple_cons(){} + default_constructible_tuple_cons(const tuples::null_type&){} +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif /* BOOST_MSVC */ + +#endif diff --git a/include/boost/multi_index/detail/has_tag.hpp b/include/boost/multi_index/detail/has_tag.hpp new file mode 100644 index 0000000..51ad590 --- /dev/null +++ b/include/boost/multi_index/detail/has_tag.hpp @@ -0,0 +1,38 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_HAS_TAG_HPP +#define BOOST_MULTI_INDEX_DETAIL_HAS_TAG_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* determines whether an index type has a given tag in its tag list */ + +template +struct has_tag +{ + template + struct apply:mpl::contains + { + }; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/header_holder.hpp b/include/boost/multi_index/detail/header_holder.hpp new file mode 100644 index 0000000..ef0a9d4 --- /dev/null +++ b/include/boost/multi_index/detail/header_holder.hpp @@ -0,0 +1,49 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_HEADER_HOLDER_HPP +#define BOOST_MULTI_INDEX_DETAIL_HEADER_HOLDER_HPP + +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* An utility class derived from base_from_member used to hold + * a pointer to the header node. The base from member idiom is used + * because index classes, which are superclasses of multi_index_container, + * need this header in construction time. + * The allocation is made by the allocator of the multi_index_container + * class --hence, this allocator needs also be stored resorting + * to the base from member trick. + */ + +template +struct header_holder:base_from_member,private noncopyable +{ + header_holder():super(final().allocate_node()){} + ~header_holder(){final().deallocate_node(super::member);} + +private: + typedef base_from_member super; + Final& final(){return *static_cast(this);} +}; + + + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_base.hpp b/include/boost/multi_index/detail/index_base.hpp new file mode 100644 index 0000000..cec41d2 --- /dev/null +++ b/include/boost/multi_index/detail/index_base.hpp @@ -0,0 +1,133 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_BASE_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_BASE_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* The role of this class is threefold: + * - tops the linear hierarchy of indices. + * - terminates some cascading backbone function calls (insert_, etc.), + * - grants access to the backbone functions of the final + * multi_index_container class (for access restriction reasons, these + * cannot be called directly from the index classes.) + */ + +template +class index_base +{ +protected: + typedef index_node_base node_type; + typedef typename multi_index_node_type< + Value,IndexSpecifierList,Allocator>::type final_node_type; + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> final_type; + typedef tuples::null_type ctor_args_list; + typedef Allocator final_allocator_type; + typedef mpl::vector0<> index_type_list; + typedef mpl::vector0<> iterator_type_list; + typedef mpl::vector0<> const_iterator_type_list; + typedef copy_map copy_map_type; + +private: + typedef typename call_traits::param_type value_param_type; + +protected: + explicit index_base(const ctor_args_list&,const Allocator&){} + + void copy_( + const index_base&,const copy_map_type&) + {} + + node_type* insert_(value_param_type v,node_type* x) + { + detail::allocator::construct(&x->value,v); + return x; + } + + node_type* insert_(value_param_type v,node_type*,node_type* x) + { + detail::allocator::construct(&x->value,v); + return x; + } + + void erase_(node_type* x) + { + detail::allocator::destroy(&x->value); + } + + void swap_(index_base&){} + + bool replace_(value_param_type v,node_type* x) + { + x->value=v; + return true; + } + + bool modify_(node_type*){return true;} + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + /* invariant stuff */ + + bool invariant_()const{return true;} +#endif + + /* access to backbone memfuns of Final class */ + + final_type& final(){return *static_cast(this);} + const final_type& final()const{return *static_cast(this);} + + final_node_type* final_header()const{return final().header();} + + bool final_empty_()const{return final().empty_();} + std::size_t final_size_()const{return final().size_();} + std::size_t final_max_size_()const{return final.max_size_();} + + std::pair final_insert_(value_param_type x) + {return final().insert_(x);} + std::pair final_insert_( + value_param_type x,final_node_type* position) + {return final().insert_(x,position);} + + void final_erase_(final_node_type* x){final().erase_(x);} + void final_swap_(final_type& x){final().swap_(x);} + bool final_replace_( + value_param_type k,final_node_type* x) + {return final().replace_(k,x);} + + template + bool final_modify_(Modifier mod,final_node_type* x) + {return final().modify_(mod,x);} + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + void final_check_invariant_()const{final().check_invariant_();} +#endif +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_iterator.hpp b/include/boost/multi_index/detail/index_iterator.hpp new file mode 100644 index 0000000..934624f --- /dev/null +++ b/include/boost/multi_index/detail/index_iterator.hpp @@ -0,0 +1,140 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_ITERATOR_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_ITERATOR_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* An iterator template for nodes of multi_index::detail::index. + * Built with the aid boost::bidirectional_iterator_helper from + * boost/operators.hpp. + */ + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +template +class index_iterator: + public boost::bidirectional_iterator_helper< + index_iterator, + typename Node::value_type, + std::ptrdiff_t, + const typename Node::value_type*, + const typename Node::value_type&>, + public safe_iterator > +#else +template +class index_iterator: + public boost::bidirectional_iterator_helper< + index_iterator, + typename Node::value_type, + std::ptrdiff_t, + const typename Node::value_type*, + const typename Node::value_type&>, + public safe_iterator +#endif +#else +template +class index_iterator: + public boost::bidirectional_iterator_helper< + index_iterator, + typename Node::value_type, + std::ptrdiff_t, + const typename Node::value_type*, + const typename Node::value_type&> +#endif + +{ +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +public: + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef index_proxy container_type; +#else + typedef Container container_type; +#endif + +private: + typedef safe_iterator safe_super; + +public: + index_iterator():node(0){} + index_iterator(Node* node_,container_type* cont_): + safe_super(cont_),node(node_){} + + index_iterator& operator=(const index_iterator& x) + { + safe_super::operator=(x); + node=x.node; + return *this; + } + +#else +public: + index_iterator(){} + index_iterator(Node* node_):node(node_){} +#endif + + + const typename Node::value_type& operator*()const + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(*this); + return node->value; + } + + index_iterator& operator++() + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this); + BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(*this); + Node::increment(node); + return *this; + } + + index_iterator& operator--() + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(*this); + BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(*this); + Node::decrement(node); + return *this; + } + + friend bool operator==(const index_iterator& x,const index_iterator& y) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(x); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(y); + BOOST_MULTI_INDEX_CHECK_SAME_OWNER(x,y); + return x.node==y.node; + } + + /* get_node is not to be used by the user */ + + Node* get_node()const{return node;} + +private: + Node* node; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_iterator_fwd.hpp b/include/boost/multi_index/detail/index_iterator_fwd.hpp new file mode 100644 index 0000000..4c63237 --- /dev/null +++ b/include/boost/multi_index/detail/index_iterator_fwd.hpp @@ -0,0 +1,40 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_ITERATOR_FWD_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_ITERATOR_FWD_HPP + +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +template +class index_iterator; +#else +template +class index_iterator; +#endif +#else +template +class index_iterator; +#endif + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_node_base.hpp b/include/boost/multi_index/detail/index_node_base.hpp new file mode 100644 index 0000000..c3fde3c --- /dev/null +++ b/include/boost/multi_index/detail/index_node_base.hpp @@ -0,0 +1,39 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_NODE_BASE_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_NODE_BASE_HPP + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* index_node_base tops the node hierarchy of multi_index_container. It holds + * the value of the element contained. + */ + +template +struct index_node_base +{ + typedef Value value_type; + value_type value; + +private: + index_node_base(); + /* this class is not intended to be cted, merely allocated */ +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/index_proxy.hpp b/include/boost/multi_index/detail/index_proxy.hpp new file mode 100644 index 0000000..7d671dd --- /dev/null +++ b/include/boost/multi_index/detail/index_proxy.hpp @@ -0,0 +1,78 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INDEX_PROXY_HPP +#define BOOST_MULTI_INDEX_DETAIL_INDEX_PROXY_HPP + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#include /* keep it first to prevent nasty warns in MSVC */ +#include + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* In safe mode, index iterators are derived from safe_iterator, + * where Index is the type of the index where the iterator belongs. Due + * to the long symbol names of indices, MSVC++ 6.0 often issues a + * LNK1179 (duplicate comdat) error. To workaround this problem, + * index_proxy is used instead. index_proxy acts as an index + * over nodes of type Node in all aspects relevant to safe_iterator, and + * its shorter symbol name makes life easier for MSVC++ 6.0. + */ + +template +class index_proxy:public safe_container > +{ +protected: + index_proxy(Node* header_):header(header_){} + + void swap(index_proxy& x) + { + std::swap(header,x.header); + safe_container >::swap(x); + } + +public: + typedef index_iterator iterator; + typedef index_iterator const_iterator; + + index_iterator begin()const + { + return index_iterator( + Node::begin(header),const_cast(this)); + } + + index_iterator end()const + { + return index_iterator( + Node::end(header),const_cast(this)); + } + +private: + Node* header; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif /* workaround */ + +#endif /* BOOST_MULTI_INDEX_ENABLE_SAFE_MODE */ + +#endif diff --git a/include/boost/multi_index/detail/invariant_assert.hpp b/include/boost/multi_index/detail/invariant_assert.hpp new file mode 100644 index 0000000..21f823a --- /dev/null +++ b/include/boost/multi_index/detail/invariant_assert.hpp @@ -0,0 +1,17 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_INVARIANT_ASSERT_HPP +#define BOOST_MULTI_INDEX_DETAIL_INVARIANT_ASSERT_HPP + +#if !defined(BOOST_MULTI_INDEX_INVARIANT_ASSERT) +#include +#define BOOST_MULTI_INDEX_INVARIANT_ASSERT BOOST_ASSERT +#endif + +#endif diff --git a/include/boost/multi_index/detail/is_index_list.hpp b/include/boost/multi_index/detail/is_index_list.hpp new file mode 100644 index 0000000..a5d425a --- /dev/null +++ b/include/boost/multi_index/detail/is_index_list.hpp @@ -0,0 +1,36 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_IS_INDEX_LIST_HPP +#define BOOST_MULTI_INDEX_DETAIL_IS_INDEX_LIST_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template +struct is_index_list +{ + BOOST_STATIC_CONSTANT(bool,mpl_sequence=mpl::is_sequence::value); + BOOST_STATIC_CONSTANT(bool,non_empty=!mpl::empty::value); + BOOST_STATIC_CONSTANT(bool,value=mpl_sequence&&non_empty); +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/modify_key_adaptor.hpp b/include/boost/multi_index/detail/modify_key_adaptor.hpp new file mode 100644 index 0000000..bb048e6 --- /dev/null +++ b/include/boost/multi_index/detail/modify_key_adaptor.hpp @@ -0,0 +1,45 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_MODIFY_KEY_ADAPTOR_HPP +#define BOOST_MULTI_INDEX_DETAIL_MODIFY_KEY_ADAPTOR_HPP + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Functional adaptor to resolve modify_key as a call to modify. + * Preferred over compose_f_gx and stuff cause it eliminates problems + * with references to references, dealing with function pointers, etc. + */ + +template +struct modify_key_adaptor +{ + + modify_key_adaptor(Modifier mod_,KeyFromValue kfv_):mod(mod_),kfv(kfv_){} + + void operator()(Value& x) + { + mod(kfv(x)); + } + +private: + Modifier mod; + KeyFromValue kfv; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/msvc_index_specifier.hpp b/include/boost/multi_index/detail/msvc_index_specifier.hpp new file mode 100644 index 0000000..225743e --- /dev/null +++ b/include/boost/multi_index/detail/msvc_index_specifier.hpp @@ -0,0 +1,65 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_MSVC_INDEX_SPECIFIER_HPP +#define BOOST_MULTI_INDEX_DETAIL_MSVC_INDEX_SPECIFIER_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +/* Workaround for a problem in MSVC with dependent template typedefs + * when accesing index specifiers. + * Modeled after (thanks, Aleksey!) + */ + +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template +struct msvc_index_specifier +{ + template struct fake_index_type:IndexSpecifier{}; + template<> struct fake_index_type + { + template + struct node_class{}; + + template + struct index_class{}; + }; + + template + struct result_node_class: + fake_index_type::value>:: + template node_class + { + }; + + template + struct result_index_class: + fake_index_type::value>:: + template index_class + { + }; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif /* workaround */ + +#endif diff --git a/include/boost/multi_index/detail/no_duplicate_tags.hpp b/include/boost/multi_index/detail/no_duplicate_tags.hpp new file mode 100644 index 0000000..4966450 --- /dev/null +++ b/include/boost/multi_index/detail/no_duplicate_tags.hpp @@ -0,0 +1,93 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_NO_DUPLICATE_TAGS_HPP +#define BOOST_MULTI_INDEX_DETAIL_NO_DUPLICATE_TAGS_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* no_duplicate_tags check at compile-time that a tag list + * has no duplicate tags. + * The algorithm deserves some explanation: tags + * are sequentially inserted into a mpl::set if they were + * not already present. Due to the magic of mpl::set + * (mpl::has_key is contant time), this operation takes linear + * time, and even MSVC++ 6.5 handles it gracefully (other obvious + * solutions are quadratic.) + */ + +struct duplicate_tag_mark{}; + +struct duplicate_tag_marker +{ + template + struct apply + { + typedef mpl::s_item< + typename mpl::if_,duplicate_tag_mark,Tag>::type, + MplSet + > type; + }; +}; + +template +struct no_duplicate_tags +{ + typedef typename mpl::fold< + TagList, + mpl::set0<>, + duplicate_tag_marker + >::type aux; + + BOOST_STATIC_CONSTANT( + bool,value=!(mpl::has_key::value)); +}; + +/* Variant for an index list: duplication is checked + * across all the indices. + */ + +struct duplicate_tag_list_marker +{ + template + struct apply:mpl::fold< + BOOST_DEDUCED_TYPENAME Index::tag_list, + MplSet, + duplicate_tag_marker> + { + }; +}; + +template +struct no_duplicate_tags_in_index_list +{ + typedef typename mpl::fold< + IndexList, + mpl::set0<>, + duplicate_tag_list_marker + >::type aux; + + BOOST_STATIC_CONSTANT( + bool,value=!(mpl::has_key::value)); +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/node_type.hpp b/include/boost/multi_index/detail/node_type.hpp new file mode 100644 index 0000000..1f63f76 --- /dev/null +++ b/include/boost/multi_index/detail/node_type.hpp @@ -0,0 +1,73 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_NODE_TYPE_HPP +#define BOOST_MULTI_INDEX_DETAIL_NODE_TYPE_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* MPL machinery to construct the internal node type associated to an + * index list. + */ + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +struct index_node_applier +{ + template + struct apply: + msvc_index_specifier:: + template result_node_class + { + }; +}; +#else +struct index_node_applier +{ + template + struct apply:IndexSpecifierIterator::type:: + BOOST_NESTED_TEMPLATE node_class + { + }; +}; +#endif + +template +struct multi_index_node_type +{ + BOOST_STATIC_ASSERT(detail::is_index_list::value); + + typedef typename mpl::iter_fold_backward< + IndexSpecifierList, + index_node_base, + mpl::bind2 + >::type type; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/ord_index_args.hpp b/include/boost/multi_index/detail/ord_index_args.hpp new file mode 100644 index 0000000..34ecc9e --- /dev/null +++ b/include/boost/multi_index/detail/ord_index_args.hpp @@ -0,0 +1,86 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_ARGS_HPP +#define BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_ARGS_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Oredered index specifiers can be instantiated in two forms: + * + * (ordered_unique|ordered_non_unique)< + * KeyFromValue,Compare=std::less > + * (ordered_unique|ordered_non_unique)< + * TagList,KeyFromValue,Compare=std::less > + * + * index_args implements the machinery to accept this argument-dependent + * polymorphism. + */ + +struct null_arg{}; + +template +struct not_is_null_arg +{ + BOOST_STATIC_CONSTANT(bool,value=!(is_same::value)); +}; + +template +struct index_args_default_compare +{ + typedef std::less type; +}; + +template +struct ordered_index_args +{ + typedef is_tag full_form; + + typedef typename mpl::if_< + full_form, + Arg1, + tag< > >::type tag_list_type; + typedef typename mpl::if_< + full_form, + Arg2, + Arg1>::type key_from_value_type; + typedef typename mpl::if_< + full_form, + Arg3, + Arg2>::type supplied_compare_type; + typedef typename mpl::apply_if< + is_same, + index_args_default_compare, + mpl::identity + >::type compare_type; + + BOOST_STATIC_ASSERT(is_tag::value); + BOOST_STATIC_ASSERT(not_is_null_arg::value); + BOOST_STATIC_ASSERT(not_is_null_arg::value); +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/ord_index_node.hpp b/include/boost/multi_index/detail/ord_index_node.hpp new file mode 100644 index 0000000..12ad422 --- /dev/null +++ b/include/boost/multi_index/detail/ord_index_node.hpp @@ -0,0 +1,457 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + * + * The internal implementation of red-black trees is based on that of SGI STL + * stl_tree.h file: + * + * Copyright (c) 1996,1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_NODE_HPP +#define BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_NODE_HPP + +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* definition of red-black nodes for ordered_index */ + +enum ordered_index_color{red=false,black=true}; + +struct ordered_index_node_impl +{ + ordered_index_color& color(){return color_;} + const ordered_index_color& color()const{return color_;} + ordered_index_node_impl*& parent(){return parent_;} + ordered_index_node_impl*const & parent()const{return parent_;} + ordered_index_node_impl*& left(){return left_;} + ordered_index_node_impl*const & left()const{return left_;} + ordered_index_node_impl*& right(){return right_;} + ordered_index_node_impl*const & right()const{return right_;} + + /* interoperability with index_iterator */ + + static void increment(ordered_index_node_impl*& x) + { + if(x->right()){ + x=x->right(); + while(x->left())x=x->left(); + } + else{ + ordered_index_node_impl* y=x->parent(); + while(x==y->right()){ + x=y; + y=y->parent(); + } + if(x->right()!=y)x=y; + } + } + + static void decrement(ordered_index_node_impl*& x) + { + if(x->color()==red&&x->parent()->parent()==x){ + x=x->right(); + } + else if(x->left()){ + ordered_index_node_impl* y=x->left(); + while(y->right())y=y->right(); + x=y; + }else{ + ordered_index_node_impl* y=x->parent(); + while(x==y->left()){ + x=y; + y=y->parent(); + } + x=y; + } + } + + /* interoperability with index_proxy */ + + static ordered_index_node_impl* begin(ordered_index_node_impl* header) + { + return header->left(); + } + + static ordered_index_node_impl* end(ordered_index_node_impl* header) + { + return header; + } + + /* algorithmic stuff */ + + static void rotate_left( + ordered_index_node_impl* x,ordered_index_node_impl*& root) + { + ordered_index_node_impl* y=x->right(); + x->right()=y->left(); + if(y->left())y->left()->parent()=x; + y->parent()=x->parent(); + + if(x==root) root=y; + else if(x==x->parent()->left())x->parent()->left()=y; + else x->parent()->right()=y; + y->left()=x; + x->parent()=y; + } + + static ordered_index_node_impl* minimum(ordered_index_node_impl* x) + { + while(x->left())x=x->left(); + return x; + } + + static ordered_index_node_impl* maximum(ordered_index_node_impl* x) + { + while(x->right())x=x->right(); + return x; + } + + static void rotate_right( + ordered_index_node_impl* x,ordered_index_node_impl*& root) + { + ordered_index_node_impl* y=x->left(); + x->left()=y->right(); + if(y->right())y->right()->parent()=x; + y->parent()=x->parent(); + + if(x==root) root=y; + else if(x==x->parent()->right())x->parent()->right()=y; + else x->parent()->left()=y; + y->right()=x; + x->parent()=y; + } + + static void rebalance( + ordered_index_node_impl* x,ordered_index_node_impl*& root) + { + x->color()=red; + while(x!=root&&x->parent()->color()==red){ + if(x->parent()==x->parent()->parent()->left()){ + ordered_index_node_impl* y=x->parent()->parent()->right(); + if(y&&y->color()==red){ + x->parent()->color()=black; + y->color()=black; + x->parent()->parent()->color()=red; + x=x->parent()->parent(); + } + else{ + if(x==x->parent()->right()){ + x=x->parent(); + rotate_left(x,root); + } + x->parent()->color()=black; + x->parent()->parent()->color()=red; + rotate_right(x->parent()->parent(),root); + } + } + else{ + ordered_index_node_impl* y=x->parent()->parent()->left(); + if(y&&y->color()==red){ + x->parent()->color()=black; + y->color()=black; + x->parent()->parent()->color()=red; + x=x->parent()->parent(); + } + else{ + if(x==x->parent()->left()){ + x=x->parent(); + rotate_right(x,root); + } + x->parent()->color()=black; + x->parent()->parent()->color()=red; + rotate_left(x->parent()->parent(),root); + } + } + } + root->color()=black; + } + + static ordered_index_node_impl* rebalance_for_erase( + ordered_index_node_impl* z,ordered_index_node_impl*& root, + ordered_index_node_impl*& leftmost,ordered_index_node_impl*& rightmost) + { + ordered_index_node_impl* y=z; + ordered_index_node_impl* x=0; + ordered_index_node_impl* x_parent=0; + if(y->left()==0){ /* z has at most one non-null child. y==z. */ + x=y->right(); /* x might be null */ + } + else{ + if(y->right()==0) { /* z has exactly one non-null child. y==z. */ + x=y->left(); /* x is not null */ + } + else{ /* z has two non-null children. Set y to */ + y=y->right(); /* z's successor. x might be null. */ + while(y->left())y=y->left(); + x=y->right(); + } + } + if(y!=z){ + z->left()->parent()=y; /* relink y in place of z. y is z's successor */ + y->left()=z->left(); + if(y!=z->right()){ + x_parent=y->parent(); + if(x) x->parent()=y->parent(); + y->parent()->left()=x; /* y must be a child of left */ + y->right()=z->right(); + z->right()->parent()=y; + } + else{ + x_parent=y; + } + + if(root==z) root=y; + else if(z->parent()->left()==z)z->parent()->left()=y; + else z->parent()->right()=y; + y->parent()=z->parent(); + std::swap(y->color(),z->color()); + y=z; /* y now points to node to be actually deleted */ + } + else{ /* y==z */ + x_parent=y->parent(); + if(x)x->parent()=y->parent(); + if(root==z){ + root=x; + } + else{ + if(z->parent()->left()==z)z->parent()->left()=x; + else z->parent()->right()=x; + } + if(leftmost==z){ + if(z->right()==0){ /* z->left() must be null also */ + leftmost=z->parent(); + } + else{ + leftmost=minimum(x); /* makes leftmost==header if z==root */ + } + } + if(rightmost==z){ + if(z->left()==0){ /* z->right() must be null also */ + rightmost=z->parent(); + } + else{ /* x==z->left() */ + rightmost=maximum(x); /* makes rightmost==header if z==root */ + } + } + } + if(y->color()!=red){ + while(x!=root&&(x==0 || x->color()==black)){ + if(x==x_parent->left()){ + ordered_index_node_impl* w=x_parent->right(); + if(w->color()==red){ + w->color()=black; + x_parent->color()=red; + rotate_left(x_parent,root); + w=x_parent->right(); + } + if((w->left()==0||w->left()->color()==black) && + (w->right()==0||w->right()->color()==black)){ + w->color()=red; + x=x_parent; + x_parent=x_parent->parent(); + } + else{ + if(w->right()==0 + || w->right()->color()==black){ + if(w->left()) w->left()->color()=black; + w->color()=red; + rotate_right(w,root); + w=x_parent->right(); + } + w->color()=x_parent->color(); + x_parent->color()=black; + if(w->right())w->right()->color()=black; + rotate_left(x_parent,root); + break; + } + } + else{ /* same as above,with right <-> left */ + ordered_index_node_impl* w=x_parent->left(); + if(w->color()==red){ + w->color()=black; + x_parent->color()=red; + rotate_right(x_parent,root); + w=x_parent->left(); + } + if((w->right()==0||w->right()->color()==black) && + (w->left()==0||w->left()->color()==black)){ + w->color()=red; + x=x_parent; + x_parent=x_parent->parent(); + } + else{ + if(w->left()==0||w->left()->color()==black){ + if(w->right())w->right()->color()=black; + w->color()=red; + rotate_left(w,root); + w=x_parent->left(); + } + w->color()=x_parent->color(); + x_parent->color()=black; + if(w->left())w->left()->color()=black; + rotate_right(x_parent,root); + break; + } + } + } + if(x)x->color()=black; + } + return y; + } + + static void restore( + ordered_index_node_impl* x,ordered_index_node_impl* prior, + ordered_index_node_impl* next,ordered_index_node_impl* header) + { + if(next==header){ + header->parent()=x; + header->left()=x; + header->right()=x; + x->parent()=header; + } + else if(next->left()==0){ + next->left()=x; + x->parent()=next; + if(next==header->left()){ + header->left()=x; /* maintain leftmost pointing to min node */ + } + } + else{ /* prior->right() must be null */ + prior->right()=x; + x->parent()=prior; + if(prior==header->right()){ + header->right()=x; /* maintain rightmost pointing to max node */ + } + } + x->left()=0; + x->right()=0; + rebalance(x,header->parent()); + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + /* invariant stuff */ + + static std::size_t black_count( + ordered_index_node_impl* node,ordered_index_node_impl* root) + { + if(!node)return 0; + std::size_t sum=0; + for(;;){ + if(node->color()==black)++sum; + if(node==root)break; + node=node->parent(); + } + return sum; + } +#endif + +private: + ordered_index_node_impl(); + + ordered_index_color color_; + ordered_index_node_impl* parent_; + ordered_index_node_impl* left_; + ordered_index_node_impl* right_; +}; + +template +struct ordered_index_node_trampoline:ordered_index_node_impl{}; + +template +struct ordered_index_node:Super,ordered_index_node_trampoline +{ + ordered_index_color& color(){return impl_type::color();} + const ordered_index_color& color()const{return impl_type::color();} + ordered_index_node_impl*& parent(){return impl_type::parent();} + ordered_index_node_impl*const & parent()const{return impl_type::parent();} + ordered_index_node_impl*& left(){return impl_type::left();} + ordered_index_node_impl*const & left()const{return impl_type::left();} + ordered_index_node_impl*& right(){return impl_type::right();} + ordered_index_node_impl*const & right()const{return impl_type::right();} + + ordered_index_node_impl* impl(){return static_cast(this);} + const ordered_index_node_impl* impl()const + {return static_cast(this);} + + static ordered_index_node* from_impl(ordered_index_node_impl *x) + { + return static_cast(static_cast(x)); + } + + static const ordered_index_node* from_impl(const ordered_index_node_impl* x) + { + return static_cast( + static_cast(x)); + } + + /* interoperability with index_iterator */ + + static void increment(ordered_index_node*& x) + { + ordered_index_node_impl* xi=x->impl(); + impl_type::increment(xi); + x=from_impl(xi); + } + + static void decrement(ordered_index_node*& x) + { + ordered_index_node_impl* xi=x->impl(); + impl_type::decrement(xi); + x=from_impl(xi); + } + + /* interoperability with index_proxy */ + + static ordered_index_node* begin(ordered_index_node* header) + { + return from_impl(impl_type::begin(header->impl())); + } + + static ordered_index_node* end(ordered_index_node* header) + { + return from_impl(impl_type::end(header->impl())); + } + +private: + typedef ordered_index_node_trampoline impl_type; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/ord_index_ops.hpp b/include/boost/multi_index/detail/ord_index_ops.hpp new file mode 100644 index 0000000..be16abb --- /dev/null +++ b/include/boost/multi_index/detail/ord_index_ops.hpp @@ -0,0 +1,121 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + * + * The internal implementation of red-black trees is based on that of SGI STL + * stl_tree.h file: + * + * Copyright (c) 1996,1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_OPS_HPP +#define BOOST_MULTI_INDEX_DETAIL_ORD_INDEX_OPS_HPP + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Common code for index memfuns having templatized and + * non-templatized versions. + */ + +template< + typename Node,typename KeyFromValue, + typename CompatibleKey,typename CompatibleCompare +> +inline Node* ordered_index_find( + Node* header,const KeyFromValue& key,const CompatibleKey& x, + const CompatibleCompare& comp) +{ + Node* y=header; + Node* z=Node::from_impl(header->parent()); + + while (z){ + if(!comp(key(z->value),x)){ + y=z; + z=Node::from_impl(z->left()); + } + else z=Node::from_impl(z->right()); + } + + return (y==header||comp(x,key(y->value)))?header:y; +} + +template< + typename Node,typename KeyFromValue, + typename CompatibleKey,typename CompatibleCompare +> +inline Node* ordered_index_lower_bound( + Node* header,const KeyFromValue& key,const CompatibleKey& x, + const CompatibleCompare& comp) +{ + Node* y=header; + Node* z=Node::from_impl(header->parent()); + + while(z){ + if(!comp(key(z->value),x)){ + y=z; + z=Node::from_impl(z->left()); + } + else z=Node::from_impl(z->right()); + } + + return y; +} + +template< + typename Node,typename KeyFromValue, + typename CompatibleKey,typename CompatibleCompare +> +inline Node* ordered_index_upper_bound( + Node* header,const KeyFromValue& key,const CompatibleKey& x, + const CompatibleCompare& comp) +{ + Node* y=header; + Node* z=Node::from_impl(header->parent()); + + while(z){ + if(comp(x,key(z->value))){ + y=z; + z=Node::from_impl(z->left()); + } + else z=Node::from_impl(z->right()); + } + + return y; +} + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/prevent_eti.hpp b/include/boost/multi_index/detail/prevent_eti.hpp new file mode 100644 index 0000000..b79ad3d --- /dev/null +++ b/include/boost/multi_index/detail/prevent_eti.hpp @@ -0,0 +1,56 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_PREVENT_ETI_HPP +#define BOOST_MULTI_INDEX_DETAIL_PREVENT_ETI_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +#include +#include +#include +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +/* See + * http://www.crystalclearsoftware.com/cgi-bin/boost_wiki/wiki.pl?Effective_MPL + * Item 5.6, Beware of the 'early template instantiation' trap. + */ + +template +struct prevent_eti +{ + typedef typename mpl::if_< + mpl::aux::msvc_never_true, + mpl::integral_c, + Construct + >::type type; +}; +#else +template +struct prevent_eti +{ + typedef Construct type; +}; +#endif + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/safe_mode.hpp b/include/boost/multi_index/detail/safe_mode.hpp new file mode 100644 index 0000000..33e436e --- /dev/null +++ b/include/boost/multi_index/detail/safe_mode.hpp @@ -0,0 +1,339 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP +#define BOOST_MULTI_INDEX_DETAIL_SAFE_MODE_HPP + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +/* Safe mode machinery, in the spirit of Cay Hortmann's "Safe STL" + * (http://www.horstmann.com/safestl.html). + * In this mode, containers of type Container are derived from + * safe_container, and their corresponding iterators + * are derived from safe_iterator. These classes provide + * an internal record of which iterators are at a given moment associated + * to a given container, and properly mark the iterators as invalid + * when the container gets destroyed. + * Iterators are chained in a single attached list, whose header is + * kept by the container. More elaborate data structures would yield better + * performance, but I decided to keep complexity to a minimum since + * speed is not an issue here. + * This is not a full-fledged safe mode framework, and is only inteded + * for use within the limits of Boost.MultiIndex. + */ + +namespace safe_mode{ + +/* Invalidates all iterators equivalent to that given. Defined before + * safe_iterator_base and safe_container_base as these contain friendship + * declarations to this function. + */ + +template +inline void detach_equivalent_iterators(Iterator& it) +{ + if(it.valid()){ + Iterator *prev_,*next_; + for( + prev_=static_cast(&it.cont->header); + (next_=static_cast(prev_->next))!=0;){ + if(next_!=&it&&*next_==it){ + prev_->next=next_->next; + next_->cont=0; + } + else prev_=next_; + } + it.detach(); + } +} + +} /* namespace multi_index::safe_mode */ + +namespace detail{ + +class safe_container_base; + +class safe_iterator_base +{ +public: + bool valid()const{return cont!=0;} + inline void detach(); + +protected: + safe_iterator_base():cont(0),next(0){} + explicit safe_iterator_base(safe_container_base* cont_){attach(cont_);} + safe_iterator_base(const safe_iterator_base& it){attach(it.cont);} + + safe_iterator_base& operator=(const safe_iterator_base& it) + { + safe_container_base* new_cont=it.cont; + if(cont!=new_cont){ + detach(); + attach(new_cont); + } + return *this; + } + + ~safe_iterator_base() + { + detach(); + } + + const safe_container_base* owner()const{return cont;} + +BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS: + friend class safe_container_base; + +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + template friend + void safe_mode::detach_equivalent_iterators(Iterator&); +#endif + + inline void attach(safe_container_base* cont_); + + safe_container_base* cont; + safe_iterator_base* next; +}; + +class safe_container_base:private noncopyable +{ +public: + safe_container_base(){} + + ~safe_container_base() + { + for(safe_iterator_base* it=header.next;it;it=it->next)it->cont=0; + } + + void swap(safe_container_base& x) + { + for(safe_iterator_base* it0=header.next;it0;it0=it0->next)it0->cont=&x; + for(safe_iterator_base* it1=x.header.next;it1;it1=it1->next)it1->cont=this; + std::swap(header.cont,x.header.cont); + std::swap(header.next,x.header.next); + } + +BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS: + friend class safe_iterator_base; + +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + template friend + void safe_mode::detach_equivalent_iterators(Iterator&); +#endif + + safe_iterator_base header; +}; + +void safe_iterator_base::attach(safe_container_base* cont_) +{ + cont=cont_; + if(cont){ + next=cont->header.next; + cont->header.next=this; + } +} + +void safe_iterator_base::detach() +{ + if(cont){ + safe_iterator_base *prev_,*next_; + for(prev_=&cont->header;(next_=prev_->next)!=this;prev_=next_){} + prev_->next=next; + cont=0; + } +} + +template +class safe_container; + +template +class safe_iterator:public safe_iterator_base +{ +public: + typedef Container container_type; + + safe_iterator():safe_iterator_base(){} + explicit safe_iterator(safe_container* cont_): + safe_iterator_base(cont_){} + + const container_type* owner()const + { + return + static_cast( + static_cast*>( + safe_iterator_base::owner())); + } +}; + +template +class safe_container:public safe_container_base +{ +public: + void swap(safe_container& x){safe_container_base::swap(x);} +}; + +} /* namespace multi_index::detail */ + +namespace safe_mode{ + +/* checking routines */ + +template +inline bool check_valid_iterator(const Iterator& it) +{ + return it.valid(); +} + +template +inline bool check_dereferenceable_iterator(const Iterator& it) +{ + return it.valid()&&it!=it.owner()->end(); +} + +template +inline bool check_incrementable_iterator(const Iterator& it) +{ + return it.valid()&&it!=it.owner()->end(); +} + +template +inline bool check_decrementable_iterator(const Iterator& it) +{ + return it.valid()&&it!=it.owner()->begin(); +} + +template +inline bool check_is_owner( + const Iterator& it,const typename Iterator::container_type& cont) +{ + return it.valid()&&it.owner()==&cont; +} + +template +inline bool check_same_owner(const Iterator& it0,const Iterator& it1) +{ + return it0.valid()&&it1.valid()&&it0.owner()==it1.owner(); +} + +template +inline bool check_valid_range(const Iterator& it0,const Iterator& it1) +{ + if(!it0.valid()||!it1.valid()||it0.owner()!=it1.owner())return false; + + Iterator last=it0.owner()->end(); + if(it1==last)return true; + + for(Iterator first=it0;first!=last;++first){ + if(first==it1)return true; + } + return false; +} + +template +inline bool check_outside_range( + const Iterator& it,const Iterator& it0,const Iterator& it1) +{ + if(!it0.valid()||!it1.valid()||it0.owner()!=it1.owner())return false; + + Iterator last=it0.owner()->end(); + bool found=false; + + Iterator first=it0; + for(;first!=last;++first){ + if(first==it1)break; + + /* crucial that this check goes after previous break */ + + if(first==it)found=true; + } + if(first!=it1)return false; + return !found; +} + +template +inline bool check_different_container( + const Container& cont0,const Container& cont1) +{ + return &cont0!=&cont1; +} + +} /* namespace multi_index::safe_mode */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif /* BOOST_MULTI_INDEX_ENABLE_SAFE_MODE */ + +/* assertion macros */ + +#if !defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#undef BOOST_MULTI_INDEX_SAFE_MODE_ASSERT +#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) ((void)0) +#else +#if !defined(BOOST_MULTI_INDEX_SAFE_MODE_ASSERT) +#include +#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) BOOST_ASSERT(expr) +#endif +#endif + +#define BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_valid_iterator(it), \ + safe_mode::invalid_iterator); + +#define BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(it) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_dereferenceable_iterator(it), \ + safe_mode::not_dereferenceable_iterator); + +#define BOOST_MULTI_INDEX_CHECK_INCREMENTABLE_ITERATOR(it) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_incrementable_iterator(it), \ + safe_mode::not_incrementable_iterator); + +#define BOOST_MULTI_INDEX_CHECK_DECREMENTABLE_ITERATOR(it) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(\ + safe_mode::check_decrementable_iterator(it), \ + safe_mode::not_decrementable_iterator); + +#define BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,cont) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_is_owner(it,cont), \ + safe_mode::not_owner); + +#define BOOST_MULTI_INDEX_CHECK_SAME_OWNER(it0,it1) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(\ + safe_mode::check_same_owner(it0,it1), \ + safe_mode::not_same_owner); + +#define BOOST_MULTI_INDEX_CHECK_VALID_RANGE(it0,it1) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_valid_range(it0,it1), \ + safe_mode::invalid_range); + +#define BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(it,it0,it1) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(\ + safe_mode::check_outside_range(it,it0,it1), \ + safe_mode::inside_range); + +#define BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(cont0,cont1) \ + BOOST_MULTI_INDEX_SAFE_MODE_ASSERT( \ + safe_mode::check_different_container(cont0,cont1), \ + safe_mode::same_container); + +#endif diff --git a/include/boost/multi_index/detail/scope_guard.hpp b/include/boost/multi_index/detail/scope_guard.hpp new file mode 100644 index 0000000..8481d07 --- /dev/null +++ b/include/boost/multi_index/detail/scope_guard.hpp @@ -0,0 +1,270 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_SCOPE_GUARD_HPP +#define BOOST_MULTI_INDEX_DETAIL_SCOPE_GUARD_HPP + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Until some official version of the ScopeGuard idiom makes it into Boost, + * we locally define our own. This is a merely reformated version of + * ScopeGuard.h as defined in: + * Alexandrescu, A., Marginean, P.:"Generic: Change the Way You + * Write Exception-Safe Code - Forever", C/C++ Users Jornal, Dec 2000, + * http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/ + * with the following modifications: + * - General pretty formatting (pretty to my taste at least.) + * - Naming style changed to standard C++ library requirements. + * - safe_execute does not feature a try-catch protection, so we can + * use this even if BOOST_NO_EXCEPTIONS is defined. + * - Added scope_guard_impl4 and obj_scope_guard_impl3, (Boost.MultiIndex + * needs them). A better design would provide guards for many more + * arguments through the Boost Preprocessor Library. + * - Added scope_guard_impl_base::touch (see below.) + * - Removed RefHolder and ByRef, whose functionality is provided + * already by Boost.Ref. + * - Removed static make_guard's and make_obj_guard's, so that the code + * will work even if BOOST_NO_MEMBER_TEMPLATES is defined. This forces + * us to move some private ctors to public, though. + */ + +class scope_guard_impl_base +{ +public: + scope_guard_impl_base():dismissed_(false){} + void dismiss()const{dismissed_=true;} + + /* This helps prevent some "unused variable" warnings under, for instance, + * GCC 3.2. + */ + void touch()const{} + +protected: + ~scope_guard_impl_base(){} + + scope_guard_impl_base(const scope_guard_impl_base& other): + dismissed_(other.dismissed_) + { + other.dismiss(); + } + + template + static void safe_execute(J& j){if(!j.dismissed_)j.execute();} + + mutable bool dismissed_; + +private: + scope_guard_impl_base& operator=(const scope_guard_impl_base&); +}; + +typedef const scope_guard_impl_base& scope_guard; + +template +class scope_guard_impl0:public scope_guard_impl_base +{ +public: + scope_guard_impl0(F fun):fun_(fun){} + ~scope_guard_impl0(){safe_execute(*this);} + void execute(){fun_();} + +protected: + + F fun_; +}; + +template +inline scope_guard_impl0 make_guard(F fun) +{ + return scope_guard_impl0(fun); +} + +template +class scope_guard_impl1:public scope_guard_impl_base +{ +public: + scope_guard_impl1(F fun,P1 p1):fun_(fun),p1_(p1){} + ~scope_guard_impl1(){safe_execute(*this);} + void execute(){fun_(p1_);} + +protected: + F fun_; + const P1 p1_; +}; + +template +inline scope_guard_impl1 make_guard(F fun,P1 p1) +{ + return scope_guard_impl1(fun,p1); +} + +template +class scope_guard_impl2:public scope_guard_impl_base +{ +public: + scope_guard_impl2(F fun,P1 p1,P2 p2):fun_(fun),p1_(p1),p2_(p2){} + ~scope_guard_impl2(){safe_execute(*this);} + void execute(){fun_(p1_,p2_);} + +protected: + F fun_; + const P1 p1_; + const P2 p2_; +}; + +template +inline scope_guard_impl2 make_guard(F fun,P1 p1,P2 p2) +{ + return scope_guard_impl2(fun,p1,p2); +} + +template +class scope_guard_impl3:public scope_guard_impl_base +{ +public: + scope_guard_impl3(F fun,P1 p1,P2 p2,P3 p3):fun_(fun),p1_(p1),p2_(p2),p3_(p3){} + ~scope_guard_impl3(){safe_execute(*this);} + void execute(){fun_(p1_,p2_,p3_);} + +protected: + F fun_; + const P1 p1_; + const P2 p2_; + const P3 p3_; +}; + +template +inline scope_guard_impl3 make_guard(F fun,P1 p1,P2 p2,P3 p3) +{ + return scope_guard_impl3(fun,p1,p2,p3); +} + +template +class scope_guard_impl4:public scope_guard_impl_base +{ +public: + scope_guard_impl4(F fun,P1 p1,P2 p2,P3 p3,P4 p4): + fun_(fun),p1_(p1),p2_(p2),p3_(p3),p4_(p4){} + ~scope_guard_impl4(){safe_execute(*this);} + void execute(){fun_(p1_,p2_,p3_,p4_);} + +protected: + F fun_; + const P1 p1_; + const P2 p2_; + const P3 p3_; + const P4 p4_; +}; + +template +inline scope_guard_impl4 make_guard( + F fun,P1 p1,P2 p2,P3 p3,P4 p4) +{ + return scope_guard_impl4(fun,p1,p2,p3,p4); +} + +template +class obj_scope_guard_impl0:public scope_guard_impl_base +{ +public: + obj_scope_guard_impl0(Obj& obj,MemFun mem_fun):obj_(obj),mem_fun_(mem_fun){} + ~obj_scope_guard_impl0(){safe_execute(*this);} + void execute(){(obj_.*mem_fun_)();} + +protected: + Obj& obj_; + MemFun mem_fun_; +}; + +template +inline obj_scope_guard_impl0 make_obj_guard(Obj& obj,MemFun mem_fun) +{ + return obj_scope_guard_impl0(obj,mem_fun); +} + +template +class obj_scope_guard_impl1:public scope_guard_impl_base +{ +public: + obj_scope_guard_impl1(Obj& obj,MemFun mem_fun,P1 p1): + obj_(obj),mem_fun_(mem_fun),p1_(p1){} + ~obj_scope_guard_impl1(){safe_execute(*this);} + void execute(){(obj_.*mem_fun_)(p1_);} + +protected: + Obj& obj_; + MemFun mem_fun_; + const P1 p1_; +}; + +template +inline obj_scope_guard_impl1 make_obj_guard( + Obj& obj,MemFun mem_fun,P1 p1) +{ + return obj_scope_guard_impl1(obj,mem_fun,p1); +} + +template +class obj_scope_guard_impl2:public scope_guard_impl_base +{ +public: + obj_scope_guard_impl2(Obj& obj,MemFun mem_fun,P1 p1,P2 p2): + obj_(obj),mem_fun_(mem_fun),p1_(p1),p2_(p2) + {} + ~obj_scope_guard_impl2(){safe_execute(*this);} + void execute(){(obj_.*mem_fun_)(p1_,p2_);} + +protected: + Obj& obj_; + MemFun mem_fun_; + const P1 p1_; + const P2 p2_; +}; + +template +inline obj_scope_guard_impl2 +make_obj_guard(Obj& obj,MemFun mem_fun,P1 p1,P2 p2) +{ + return obj_scope_guard_impl2(obj,mem_fun,p1,p2); +} + +template +class obj_scope_guard_impl3:public scope_guard_impl_base +{ +public: + obj_scope_guard_impl3(Obj& obj,MemFun mem_fun,P1 p1,P2 p2,P3 p3): + obj_(obj),mem_fun_(mem_fun),p1_(p1),p2_(p2),p3_(p3) + {} + ~obj_scope_guard_impl3(){safe_execute(*this);} + void execute(){(obj_.*mem_fun_)(p1_,p2_,p3_);} + +protected: + Obj& obj_; + MemFun mem_fun_; + const P1 p1_; + const P2 p2_; + const P3 p3_; +}; + +template +inline obj_scope_guard_impl3 +make_obj_guard(Obj& obj,MemFun mem_fun,P1 p1,P2 p2,P3 p3) +{ + return obj_scope_guard_impl3(obj,mem_fun,p1,p2,p3); +} + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/seq_index_node.hpp b/include/boost/multi_index/detail/seq_index_node.hpp new file mode 100644 index 0000000..4867a19 --- /dev/null +++ b/include/boost/multi_index/detail/seq_index_node.hpp @@ -0,0 +1,197 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_SEQ_INDEX_NODE_HPP +#define BOOST_MULTI_INDEX_DETAIL_SEQ_INDEX_NODE_HPP + +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* doubly-linked node for use by sequenced_index */ + +struct sequenced_index_node_impl +{ + sequenced_index_node_impl*& prior(){return prior_;} + sequenced_index_node_impl*const & prior()const{return prior_;} + sequenced_index_node_impl*& next(){return next_;} + sequenced_index_node_impl*const & next()const{return next_;} + + /* interoperability with index_iterator */ + + static void increment(sequenced_index_node_impl*& x){x=x->next();} + static void decrement(sequenced_index_node_impl*& x){x=x->prior();} + + /* interoperability with index_proxy */ + + static sequenced_index_node_impl* begin(sequenced_index_node_impl* header) + { + return header->next(); + } + + static sequenced_index_node_impl* end(sequenced_index_node_impl* header) + { + return header; + } + + /* algorithmic stuff */ + + static void link( + sequenced_index_node_impl* x,sequenced_index_node_impl* header) + { + x->prior()=header->prior(); + x->next()=header; + x->prior()->next()=x->next()->prior()=x; + }; + + static void unlink(sequenced_index_node_impl* x) + { + x->prior()->next()=x->next(); + x->next()->prior()=x->prior(); + } + + static void relink( + sequenced_index_node_impl* position,sequenced_index_node_impl* x) + { + unlink(x); + x->prior()=position->prior(); + x->next()=position; + x->prior()->next()=x->next()->prior()=x; + } + + static void relink( + sequenced_index_node_impl* position, + sequenced_index_node_impl* x,sequenced_index_node_impl* y) + { + /* position is assumed not to be in [x,y) */ + + if(x!=y){ + sequenced_index_node_impl* z=y->prior(); + x->prior()->next()=y; + y->prior()=x->prior(); + x->prior()=position->prior(); + z->next()=position; + x->prior()->next()=x; + z->next()->prior()=z; + } + } + + static void reverse(sequenced_index_node_impl* header) + { + sequenced_index_node_impl* x=header; + do{ + sequenced_index_node_impl* y=x->next(); + std::swap(x->prior(),x->next()); + x=y; + }while(x!=header); + } + + static void swap(sequenced_index_node_impl* x,sequenced_index_node_impl* y) + { + /* This swap function does not exchange the header nodes, + * but rather their pointers. This is *not* used for implementing + * sequenced_index::swap. + */ + + if(x->next()!=x){ + if(y->next()!=y){ + std::swap(x->next(),y->next()); + std::swap(x->prior(),y->prior()); + x->next()->prior()=x->prior()->next()=x; + y->next()->prior()=y->prior()->next()=y; + } + else{ + y->next()=x->next(); + y->prior()=x->prior(); + x->next()=x->prior()=x; + y->next()->prior()=y->prior()->next()=y; + } + } + else if(y->next()!=y){ + x->next()=y->next(); + x->prior()=y->prior(); + y->next()=y->prior()=y; + x->next()->prior()=x->prior()->next()=x; + } + } + +private: + sequenced_index_node_impl(); + + sequenced_index_node_impl* prior_; + sequenced_index_node_impl* next_; +}; + +template +struct sequenced_index_node_trampoline:sequenced_index_node_impl{}; + +template +struct sequenced_index_node:Super,sequenced_index_node_trampoline +{ + sequenced_index_node_impl*& prior(){return impl_type::prior();} + sequenced_index_node_impl*const & prior()const{return impl_type::prior();} + sequenced_index_node_impl*& next(){return impl_type::next();} + sequenced_index_node_impl*const & next()const{return impl_type::next();} + + sequenced_index_node_impl* impl() + {return static_cast(this);} + const sequenced_index_node_impl* impl()const + {return static_cast(this);} + + static sequenced_index_node* from_impl(sequenced_index_node_impl *x) + {return static_cast(static_cast(x));} + static const sequenced_index_node* from_impl( + const sequenced_index_node_impl* x) + { + return static_cast( + static_cast(x)); + } + + /* interoperability with index_iterator */ + + static void increment(sequenced_index_node*& x) + { + sequenced_index_node_impl* xi=x->impl(); + impl_type::increment(xi); + x=from_impl(xi); + } + + static void decrement(sequenced_index_node*& x) + { + sequenced_index_node_impl* xi=x->impl(); + impl_type::decrement(xi); + x=from_impl(xi); + } + + /* interoperability with index_proxy */ + + static sequenced_index_node* begin(sequenced_index_node* header) + { + return from_impl(impl_type::begin(header->impl())); + } + + static sequenced_index_node* end(sequenced_index_node* header) + { + return from_impl(impl_type::end(header->impl())); + } + +private: + typedef sequenced_index_node_trampoline impl_type; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/seq_index_ops.hpp b/include/boost/multi_index/detail/seq_index_ops.hpp new file mode 100644 index 0000000..a4ce908 --- /dev/null +++ b/include/boost/multi_index/detail/seq_index_ops.hpp @@ -0,0 +1,164 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_SEQ_INDEX_OPS_HPP +#define BOOST_MULTI_INDEX_DETAIL_SEQ_INDEX_OPS_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* Common code for sequenced_index memfuns having templatized and + * non-templatized versions. + */ + +template +void sequenced_index_remove(SequencedIndex& x,Predicate pred) +{ + typedef typename SequencedIndex::iterator iterator; + iterator first=x.begin(),last=x.end(); + while(first!=last){ + if(pred(*first))x.erase(first++); + else ++first; + } +} + +template +void sequenced_index_unique(SequencedIndex& x,BinaryPredicate binary_pred) +{ + typedef typename SequencedIndex::iterator iterator; + iterator first=x.begin(); + iterator last=x.end(); + if(first!=last){ + for(iterator middle=first;++middle!=last;middle=first){ + if(binary_pred(*middle,*first))x.erase(middle); + else first=middle; + } + } +} + +template +void sequenced_index_merge(SequencedIndex& x,SequencedIndex& y,Compare comp) +{ + typedef typename SequencedIndex::iterator iterator; + if(x!=y){ + iterator first0=x.begin(),last0=x.end(); + iterator first1=y.begin(),last1=y.end(); + while(first0!=last0&&first1!=last1){ + if(comp(*first1,*first0))x.splice(first0,y,first1++); + else ++first0; + } + x.splice(last0,y,first1,last1); + } +} + +/* sorting */ + +/* auxiliary stuff */ + +template +void sequenced_index_collate( + sequenced_index_node_impl* x,sequenced_index_node_impl* y,Compare comp + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Node)) +{ + sequenced_index_node_impl* first0=x->next(); + sequenced_index_node_impl* last0=x; + sequenced_index_node_impl* first1=y->next(); + sequenced_index_node_impl* last1=y; + while(first0!=last0&&first1!=last1){ + if(comp(Node::from_impl(first1)->value,Node::from_impl(first0)->value)){ + sequenced_index_node_impl* tmp=first1->next(); + sequenced_index_node_impl::relink(first0,first1); + first1=tmp; + } + else first0=first0->next(); + } + sequenced_index_node_impl::relink(last0,first1,last1); +} + +template +void sequenced_index_sort(Node* header,Compare comp) +{ + /* Musser's mergesort, see http://www.cs.rpi.edu/~musser/gp/List/lists1.html. + * The implementation is a little convoluted: in the original code + * counter elements and carry are std::lists: here we do not want + * to use multi_index instead, so we do things at a lower level, managing + * directly the internal node representation. + * Incidentally, the implementations I've seen of this algorithm (SGI, + * Dinkumware, STLPort) are not exception-safe: this is. Moreover, we do not + * use any dynamic storage. + */ + + if(header->next()==header->impl()|| + header->next()->next()==header->impl())return; + + BOOST_STATIC_CONSTANT( + std::size_t, + max_fill=(std::size_t)std::numeric_limits::digits+1); + + aligned_storage< + sizeof(sequenced_index_node_impl)> carry_spc; + sequenced_index_node_impl& carry= + *static_cast(carry_spc.address()); + aligned_storage< + sizeof( + sequenced_index_node_impl[max_fill])> counter_spc; + sequenced_index_node_impl* counter= + static_cast(counter_spc.address()); + std::size_t fill=0; + + carry.prior()=carry.next()=&carry; + counter[0].prior()=counter[0].next()=&counter[0]; + + BOOST_TRY{ + while(header->next()!=header->impl()){ + sequenced_index_node_impl::relink(carry.next(),header->next()); + std::size_t i=0; + while(i(&carry,&counter[i++],comp); + } + sequenced_index_node_impl::swap(&carry,&counter[i]); + if(i==fill){ + ++fill; + counter[fill].prior()=counter[fill].next()=&counter[fill]; + } + } + + for(std::size_t i=1;i(&counter[i],&counter[i-1],comp); + } + sequenced_index_node_impl::swap(header->impl(),&counter[fill-1]); + } + BOOST_CATCH(...) + { + sequenced_index_node_impl::relink(header->impl(),carry.next(),&carry); + for(std::size_t i=0;i<=fill;++i){ + sequenced_index_node_impl::relink( + header->impl(),counter[i].next(),&counter[i]); + } + BOOST_RETHROW; + } + BOOST_CATCH_END +} + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/unbounded.hpp b/include/boost/multi_index/detail/unbounded.hpp new file mode 100644 index 0000000..3a76e02 --- /dev/null +++ b/include/boost/multi_index/detail/unbounded.hpp @@ -0,0 +1,35 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_UNBOUNDED_HPP +#define BOOST_MULTI_INDEX_DETAIL_UNBOUNDED_HPP + +namespace boost{ + +namespace multi_index{ + +/* dummy type and variable for use in ordered_index::range() */ + +namespace detail{ + +struct unbounded_type{}; + +} /* namespace multi_index::detail */ + +namespace{ + +detail::unbounded_type unbounded_obj=detail::unbounded_type(); +detail::unbounded_type& unbounded=unbounded_obj; + +} /* unnamed */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/detail/value_compare.hpp b/include/boost/multi_index/detail/value_compare.hpp new file mode 100644 index 0000000..4b68687 --- /dev/null +++ b/include/boost/multi_index/detail/value_compare.hpp @@ -0,0 +1,48 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_DETAIL_VALUE_COMPARE_HPP +#define BOOST_MULTI_INDEX_DETAIL_VALUE_COMPARE_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template +struct value_comparison:std::binary_function +{ + value_comparison(KeyFromValue key_=KeyFromValue(),Compare comp_=Compare()): + key(key_),comp(comp_) + { + } + + bool operator()( + typename call_traits::param_type x, + typename call_traits::param_type y) + { + return comp(key(x),key(y)); + } + +private: + KeyFromValue key; + Compare comp; +}; + +} /* namespace multi_index::detail */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/identity.hpp b/include/boost/multi_index/identity.hpp new file mode 100644 index 0000000..b001c58 --- /dev/null +++ b/include/boost/multi_index/identity.hpp @@ -0,0 +1,119 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_IDENTITY_HPP +#define BOOST_MULTI_INDEX_IDENTITY_HPP + +#include +#include +#include +#include +#include + +namespace boost{ + +template class reference_wrapper; /* fwd decl. */ + +namespace multi_index{ + +namespace detail{ + +/* identity is a do-nothing key extractor that returns the [const] Type& + * object passed. + * Additionally, identity is overloaded to support referece_wrappers + * of Type and "chained pointers" to Type's. By chained pointer to Type we + * mean a type P such that, given a p of type P + * *...n...*x is convertible to Type&, for some n>=1. + * Examples of chained pointers are raw and smart pointers, iterators and + * arbitrary combinations of these (vg. Type** or auto_ptr.) + */ + +/* NB. Some overloads of operator() have an extra dummy parameter int=0. + * This is so because MSVC++ 6.0 otherwise *incorrectly* regards these + * overloads as specializations of a previous member function template. + * Left for all compilers as it does no harm. + */ + +template +struct const_identity_base +{ + typedef Type result_type; + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type& operator()(Type& x)const + { + return x; + } + + Type& operator()(const reference_wrapper& x)const + { + return x.get(); + } + + Type& operator()( + const reference_wrapper::type>& x)const + { + return x.get(); + } +}; + +template +struct non_const_identity_base +{ + typedef Type result_type; + + /* templatized for pointer-like types */ + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + const Type& operator()(const Type& x,int=0)const + { + return x; + } + + Type& operator()(Type& x)const + { + return x; + } + + const Type& operator()(const reference_wrapper& x,int=0)const + { + return x.get(); + } + + Type& operator()(const reference_wrapper& x)const + { + return x.get(); + } +}; + +} /* namespace multi_index::detail */ + +template +struct identity: + mpl::if_c< + is_const::value, + detail::const_identity_base,detail::non_const_identity_base + >::type +{ +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/identity_fwd.hpp b/include/boost/multi_index/identity_fwd.hpp new file mode 100644 index 0000000..40b0533 --- /dev/null +++ b/include/boost/multi_index/identity_fwd.hpp @@ -0,0 +1,22 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_IDENTITY_HPP +#define BOOST_MULTI_INDEX_IDENTITY_HPP + +namespace boost{ + +namespace multi_index{ + +template struct identity; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/indexed_by.hpp b/include/boost/multi_index/indexed_by.hpp new file mode 100644 index 0000000..07f0069 --- /dev/null +++ b/include/boost/multi_index/indexed_by.hpp @@ -0,0 +1,68 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_INDEXED_BY_HPP +#define BOOST_MULTI_INDEX_INDEXED_BY_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include + +/* An alias to mpl::vector used to hide MPL from the user. + * indexed_by contains the index specifiers for instantiation + * of a multi_index_container. + */ + +/* This user_definable macro limits the number of elements of an index list; + * useful for shortening resulting symbol names (MSVC++ 6.0, for instance, + * has problems coping with very long symbol names.) + */ + +#if !defined(BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE) +#if defined(BOOST_MSVC)&&(BOOST_MSVC<1300) +#define BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE 5 +#else +#define BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE BOOST_MPL_LIMIT_VECTOR_SIZE +#endif +#endif + +#if BOOST_MULTI_INDEX_LIMIT_INDEXED_BY_SIZE +struct indexed_by: + mpl::vector +{ +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#undef BOOST_MULTI_INDEX_INDEXED_BY_TEMPLATE_PARM +#undef BOOST_MULTI_INDEX_INDEXED_BY_SIZE + +#endif diff --git a/include/boost/multi_index/key_extractors.hpp b/include/boost/multi_index/key_extractors.hpp new file mode 100644 index 0000000..551b31e --- /dev/null +++ b/include/boost/multi_index/key_extractors.hpp @@ -0,0 +1,17 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_KEY_EXTRACTORS_HPP +#define BOOST_MULTI_INDEX_KEY_EXTRACTORS_HPP + +#include +#include +#include +#include + +#endif diff --git a/include/boost/multi_index/mem_fun.hpp b/include/boost/multi_index/mem_fun.hpp new file mode 100644 index 0000000..fd599f7 --- /dev/null +++ b/include/boost/multi_index/mem_fun.hpp @@ -0,0 +1,171 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_MEM_FUN_HPP +#define BOOST_MULTI_INDEX_MEM_FUN_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include + +namespace boost{ + +template class reference_wrapper; /* fwd decl. */ + +namespace multi_index{ + +/* mem_fun implements a read-only key extractor based on a given non-const + * member function of a class. + * const_mem_fun does the same for const member functions. + * Additionally, mem_fun and const_mem_fun are overloaded to support + * referece_wrappers of T and "chained pointers" to T's. By chained pointer + * to T we mean a type P such that, given a p of Type P + * *...n...*x is convertible to T&, for some n>=1. + * Examples of chained pointers are raw and smart pointers, iterators and + * arbitrary combinations of these (vg. T** or auto_ptr.) + */ + +template +struct const_mem_fun +{ + typedef typename remove_reference::type result_type; + + template + Type operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type operator()(const Class& x)const + { + return (x.*PtrToMemberFunction)(); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +template +struct mem_fun +{ + typedef typename remove_reference::type result_type; + + template + Type operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type operator()(Class& x)const + { + return (x.*PtrToMemberFunction)(); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +/* MSVC++ 6.0 has problems with const member functions as non-type template + * parameters, somehow it takes them as non-const. mem_fun_explicit workarounds + * this defficiency by accepting an extra type parameter that specifies the + * signature of he member function. The workaround was found at: + * Daniel, C.:"Re: weird typedef problem in VC", + * news:microsoft.public.vc.language, 21st nov 2002, + * http://groups.google.com/groups? + * hl=en&lr=&ie=UTF-8&selm=ukwvg3O0BHA.1512%40tkmsftngp05 + */ + +template< + class Class,typename Type, + typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction> +struct const_mem_fun_explicit +{ + typedef typename remove_reference::type result_type; + + template + Type operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type operator()(const Class& x)const + { + return (x.*PtrToMemberFunction)(); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +template< + class Class,typename Type, + typename PtrToMemberFunctionType,PtrToMemberFunctionType PtrToMemberFunction> +struct mem_fun_explicit +{ + typedef typename remove_reference::type result_type; + + template + Type operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type operator()(Class& x)const + { + return (x.*PtrToMemberFunction)(); + } + + Type operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +/* BOOST_MULTI_INDEX_CONST_MEM_FUN and BOOST_MULTI_INDEX_MEM_FUN resolve to + * mem_fun_explicit for MSVC++ 6.0 and to [const_]mem_fun otherwise. + */ + +#if defined(BOOST_MSVC)&&(BOOST_MSVC<1300) + +#define BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName) \ +::boost::multi_index::const_mem_fun_explicit<\ + Class,Type,Type (Class::*)()const,&Class::MemberFunName> +#define BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName) \ +::boost::multi_index::mem_fun_explicit<\ + Class,Type,Type (Class::*)(),&Class::MemberFunName> + +#else + +#define BOOST_MULTI_INDEX_CONST_MEM_FUN(Class,Type,MemberFunName) \ +::boost::multi_index::const_mem_fun +#define BOOST_MULTI_INDEX_MEM_FUN(Class,Type,MemberFunName) \ +::boost::multi_index::mem_fun + +#endif + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/member.hpp b/include/boost/multi_index/member.hpp new file mode 100644 index 0000000..8ec15a0 --- /dev/null +++ b/include/boost/multi_index/member.hpp @@ -0,0 +1,228 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_MEMBER_HPP +#define BOOST_MULTI_INDEX_MEMBER_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include + +namespace boost{ + +template class reference_wrapper; /* fwd decl. */ + +namespace multi_index{ + +namespace detail{ + +/* member is a read/write key extractor for accessing a given + * member of a class. + * Additionally, member is overloaded to support referece_wrappers + * of T and "chained pointers" to T's. By chained pointer to T we mean + * a type P such that, given a p of Type P + * *...n...*x is convertible to T&, for some n>=1. + * Examples of chained pointers are raw and smart pointers, iterators and + * arbitrary combinations of these (vg. T** or auto_ptr.) + */ + +/* NB. Some overloads of operator() have an extra dummy parameter int=0. + * This is so because MSVC++ 6.0 otherwise *incorrectly* regards these + * overloads as specializations of a previous member function template. + * Left for all compilers as it does no harm. + */ + +template +struct const_member_base +{ + typedef Type result_type; + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type& operator()(const Class& x)const + { + return x.*PtrToMember; + } + + Type& operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } + + Type& operator()(const reference_wrapper x)const + { + return operator()(x.get()); + } +}; + +template +struct non_const_member_base +{ + typedef Type result_type; + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + const Type& operator()(const Class& x,int=0)const + { + return x.*PtrToMember; + } + + Type& operator()(Class& x)const + { + return x.*PtrToMember; + } + + const Type& operator()(const reference_wrapper& x,int=0)const + { + return operator()(x.get()); + } + + Type& operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +} /* namespace multi_index::detail */ + +template +struct member: + mpl::if_c< + is_const::value, + detail::const_member_base, + detail::non_const_member_base + >::type +{ +}; + +namespace detail{ + +/* MSVC++ 6.0 does not support properly pointers to members as + * non-type template arguments, as reported in + * http://support.microsoft.com/default.aspx?scid=kb;EN-US;249045 + * We provide an alternative to member<> accepting offsets instead + * of pointers to members. This happens to work even for non-POD + * types (although the standard forbids use of offsetof on these), + * so it serves as a workaround in this compiler for all practical + * purposes. + * Surprisingly enough, other compilers (Intel C++ 7.1 at least) + * have similar bugs. This replacement of member<> can be used for + * them too. + */ + +template +struct const_member_offset_base +{ + typedef Type result_type; + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + Type& operator()(const Class& x)const + { + return *static_cast( + static_cast( + static_cast( + static_cast(&x))+OffsetOfMember)); + } + + Type& operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } + + Type& operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +template +struct non_const_member_offset_base +{ + typedef Type result_type; + + template + Type& operator()(const ChainedPtr& x)const + { + return operator()(*x); + } + + const Type& operator()(const Class& x,int=0)const + { + return *static_cast( + static_cast( + static_cast( + static_cast(&x))+OffsetOfMember)); + } + + Type& operator()(Class& x)const + { + return *static_cast( + static_cast( + static_cast(static_cast(&x))+OffsetOfMember)); + } + + const Type& operator()(const reference_wrapper& x,int=0)const + { + return operator()(x.get()); + } + + Type& operator()(const reference_wrapper& x)const + { + return operator()(x.get()); + } +}; + +} /* namespace multi_index::detail */ + +template +struct member_offset: + mpl::if_c< + is_const::value, + detail::const_member_offset_base, + detail::non_const_member_offset_base + >::type +{ +}; + +/* A proposal has been issued to add a defect macro in Boost.Config to detect + * this problem with pointer to members as template arguments. While + * the macro gets into the library, we follow our own heuristics in order to + * define BOOST_MULTI_INDEX_MEMBER as a convenient wrapper of member<> and + * member_offset<> + */ + +#if defined(BOOST_NO_POINTER_TO_MEMBER_TEMPLATE_PARAMETERS) ||\ + defined(BOOST_MSVC)&&(BOOST_MSVC<1300) ||\ + defined(BOOST_INTEL_CXX_VERSION)&&defined(_MSC_VER)&&\ + (BOOST_INTEL_CXX_VERSION<=700) +#define BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName) \ +::boost::multi_index::member_offset +#else +#define BOOST_MULTI_INDEX_MEMBER(Class,Type,MemberName) \ +::boost::multi_index::member +#endif + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/ordered_index.hpp b/include/boost/multi_index/ordered_index.hpp new file mode 100644 index 0000000..ffdd185 --- /dev/null +++ b/include/boost/multi_index/ordered_index.hpp @@ -0,0 +1,1124 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + * + * The internal implementation of red-black trees is based on that of SGI STL + * stl_tree.h file: + * + * Copyright (c) 1996,1997 + * Silicon Graphics Computer Systems, Inc. + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Silicon Graphics makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Permission to use, copy, modify, distribute and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. Hewlett-Packard Company makes no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + */ + +#ifndef BOOST_MULTI_INDEX_ORDERED_INDEX_HPP +#define BOOST_MULTI_INDEX_ORDERED_INDEX_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) +#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT \ + detail::scope_guard BOOST_JOIN(check_invariant_,__LINE__)= \ + detail::make_obj_guard(*this,&ordered_index::check_invariant_); \ + BOOST_JOIN(check_invariant_,__LINE__).touch(); +#else +#define BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* ordered_index adds a layer of indexing to a given Super */ + +/* Most of the implementation of unique and non-unique indices is + * shared. We tell from one another on instantiation time by using + * these tags. + */ + +struct ordered_unique_tag{}; +struct ordered_non_unique_tag{}; + +template< + typename KeyFromValue,typename Compare, + typename Super,typename TagList,typename Category +> + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +class ordered_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super, + public index_proxy > +#else +class ordered_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super, + public safe_container< + ordered_index > +#endif +#else +class ordered_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super +#endif + +{ +protected: + typedef ordered_index_node< + typename Super::node_type> node_type; + +public: + /* types */ + + typedef typename KeyFromValue::result_type key_type; + typedef typename node_type::value_type value_type; + typedef KeyFromValue key_from_value; + typedef Compare key_compare; + typedef value_comparison< + value_type,KeyFromValue,Compare> value_compare; + typedef tuple ctor_args; + typedef typename Super::final_allocator_type allocator_type; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#else + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#endif +#else + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#endif + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename + boost::reverse_iterator reverse_iterator; + typedef typename + boost::reverse_iterator const_reverse_iterator; + typedef typename TagList::type tag_list; + +protected: + typedef typename Super::final_node_type final_node_type; + typedef tuples::cons< + ctor_args, + typename Super::ctor_args_list> ctor_args_list; + typedef typename mpl::push_front< + typename Super::index_type_list, + ordered_index>::type index_type_list; + typedef typename mpl::push_front< + typename Super::iterator_type_list, + iterator>::type iterator_type_list; + typedef typename mpl::push_front< + typename Super::const_iterator_type_list, + const_iterator>::type const_iterator_type_list; + typedef typename Super::copy_map_type copy_map_type; + +private: +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef index_proxy< + ordered_index_node< + typename Super::node_type> > safe_super; +#else + typedef safe_container safe_super; +#endif +#endif + + typedef typename call_traits< + value_type>::param_type value_param_type; + typedef typename call_traits< + key_type>::param_type key_param_type; + +public: + + /* construct/copy/destroy + * Default and copy ctors are in the protected section as indices are + * not supposed to be created on their own. No range ctor either. + */ + + ordered_index& operator=( + const ordered_index& x) + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + this->final()=x.final(); + return *this; + } + + allocator_type get_allocator()const + { + return this->final().get_allocator(); + } + + /* iterators */ + + iterator begin(){return make_iterator(leftmost());} + const_iterator begin()const{return make_iterator(leftmost());} + iterator end(){return make_iterator(header());} + const_iterator end()const{return make_iterator(header());} + reverse_iterator rbegin(){return make_reverse_iterator(end());} + const_reverse_iterator rbegin()const{return make_reverse_iterator(end());} + reverse_iterator rend(){return make_reverse_iterator(begin());} + const_reverse_iterator rend()const{return make_reverse_iterator(begin());} + + /* capacity */ + + bool empty()const{return this->final_empty_();} + size_type size()const{return this->final_size_();} + size_type max_size()const{return this->final_max_size_();} + + /* modifiers */ + + std::pair insert(value_param_type x) + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + std::pair p=this->final_insert_(x); + /* "this->" not required by std, but CW9.2 seems to need it */ + + return std::pair(make_iterator(p.first),p.second); + } + + iterator insert(iterator position,value_param_type x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + std::pair p=final_insert_( + x,static_cast(position.get_node())); + return make_iterator(p.first); + } + + template + void insert(InputIterator first,InputIterator last) + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + iterator hint=end(); + for(;first!=last;++first)hint=insert(hint,*first); + } + + void erase(iterator position) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + /* MSVC++ 6.0 optimizer on safe mode code chokes if this + * this is not added. Left it for all compilers as it does no + * harm. + */ + + position.detach(); +#endif + + final_erase_(static_cast(position.get_node())); + } + + size_type erase(key_param_type x) + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + std::pair p=equal_range(x); + size_type s=0; + while(p.first!=p.second){ + erase(p.first++); + ++s; + } + return s; + } + + void erase(iterator first,iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,*this); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,*this); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + while(first!=last){ + erase(first++); + } + } + + bool replace(iterator position,value_param_type x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + return final_replace_( + x,static_cast(position.get_node())); + } + + template + bool modify(iterator position,Modifier mod) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + /* MSVC++ 6.0 optimizer on safe mode code chokes if this + * this is not added. Left it for all compilers as it does no + * harm. + */ + + position.detach(); +#endif + + return final_modify_( + mod,static_cast(position.get_node())); + } + + template + bool modify_key(iterator position,Modifier mod) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + return modify( + position,modify_key_adaptor(mod,key)); + } + + void swap(ordered_index& x) + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + final_swap_(x.final()); + } + + void clear() + { + BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT; + erase(begin(),end()); + } + + /* observers */ + + key_from_value key_extractor()const{return key;} + key_compare key_comp()const{return comp;} + value_compare value_comp()const{return value_compare(key,comp);} + + /* set operations */ + + /* no need to provide non-const versions as + * ordered_index::iterator==ordered_index::const_iterator + */ + + template + const_iterator find(const CompatibleKey& x)const + { + return make_iterator(ordered_index_find(header(),key,x,comp)); + } + + template + const_iterator find( + const CompatibleKey& x,const CompatibleCompare& comp)const + { + return make_iterator(ordered_index_find(header(),key,x,comp)); + } + + template + size_type count(const CompatibleKey& x)const + { + return count(x,comp); + } + + template + size_type count(const CompatibleKey& x,const CompatibleCompare& comp)const + { + std::pair p=equal_range(x,comp); + size_type n=std::distance(p.first,p.second); + return n; + } + + template + const_iterator lower_bound(const CompatibleKey& x)const + { + return make_iterator(ordered_index_lower_bound(header(),key,x,comp)); + } + + template + const_iterator lower_bound( + const CompatibleKey& x,const CompatibleCompare& comp)const + { + return make_iterator(ordered_index_lower_bound(header(),key,x,comp)); + } + + template + const_iterator upper_bound(const CompatibleKey& x)const + { + return make_iterator(ordered_index_upper_bound(header(),key,x,comp)); + } + + template + const_iterator upper_bound( + const CompatibleKey& x,const CompatibleCompare& comp)const + { + return make_iterator(ordered_index_upper_bound(header(),key,x,comp)); + } + + template + std::pair equal_range( + const CompatibleKey& x)const + { + return equal_range(x,comp); + } + + template + std::pair equal_range( + const CompatibleKey& x,const CompatibleCompare& comp)const + { + return std::pair( + lower_bound(x,comp),upper_bound(x,comp)); + } + + /* range */ + + /* no need to provide non-const version as + * ordered_index::iterator==ordered_index::const_iterator + */ + + template + std::pair + range(LowerBounder lower,UpperBounder upper)const + { + std::pair p( + lower_range(lower),upper_range(upper)); + if(p.second!=end()&&(p.first==end()||comp(key(*p.second),key(*p.first)))){ + p.second=p.first; + } + return p; + } + +BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: + ordered_index(const ctor_args_list& args_list,const allocator_type& al): + Super(args_list.get_tail(),al), + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)&&\ + BOOST_WORKAROUND(BOOST_MSVC,<1300) + safe_super(final_header()), +#endif + + key(tuples::get<0>(args_list.get_head())), + comp(tuples::get<1>(args_list.get_head())) + { + empty_initialize(); + } + + ordered_index( + const ordered_index& x): + Super(x), + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)&&\ + BOOST_WORKAROUND(BOOST_MSVC,<1300) + safe_super(final_header()), +#endif + + key(x.key), + comp(x.comp) + { + /* Copy ctor just takes the key and compare objects from x. The rest is + * done in subsequent call to copy_(). + */ + } + + ~ordered_index() + { + /* the container is guaranteed to be empty by now */ + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + iterator make_iterator(node_type* node){return iterator(node,this);} + const_iterator make_iterator(node_type* node)const + {return const_iterator(node,const_cast(this));} +#else + iterator make_iterator(node_type* node){return iterator(node);} + const_iterator make_iterator(node_type* node)const + {return const_iterator(node);} +#endif + + void copy_( + const ordered_index& x, + const copy_map_type& map) + { + if(!x.root()){ + empty_initialize(); + } + else{ + header()->color()=x.header()->color(); + + node_type* root_cpy=map.find(static_cast(x.root())); + header()->parent()=root_cpy->impl(); + + node_type* leftmost_cpy=map.find( + static_cast(x.leftmost())); + header()->left()=leftmost_cpy->impl(); + + node_type* rightmost_cpy=map.find( + static_cast(x.rightmost())); + header()->right()=rightmost_cpy->impl(); + + typedef typename copy_map_type::const_iterator copy_map_iterator; + for(copy_map_iterator it=map.begin();it!=map.end();++it){ + node_type* org=it->first; + node_type* cpy=it->second; + + cpy->color()=org->color(); + + ordered_index_node_impl* parent_org=org->parent(); + if(!parent_org)cpy->parent()=0; + else{ + node_type* parent_cpy=map.find( + static_cast(node_type::from_impl(parent_org))); + cpy->parent()=parent_cpy->impl(); + if(parent_org->left()==org->impl()){ + parent_cpy->left()=cpy->impl(); + } + else if(parent_org->right()==org->impl()){ + /* header() does not satisfy this nor the previous check */ + parent_cpy->right()=cpy->impl(); + } + } + + if(!org->left())cpy->left()=0; + if(!org->right())cpy->right()=0; + } + } + + Super::copy_(x,map); + } + + node_type* insert_(value_param_type v,node_type* x) + { + node_type* res=link2(key(v),x,Category()); + if(res!=x)return res; + else{ + BOOST_TRY{ + res=static_cast(Super::insert_(v,x)); + if(res!=x){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + } + return res; + } + BOOST_CATCH(...){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + } + + node_type* insert_(value_param_type v,node_type* position,node_type* x) + { + node_type* res=link3(key(v),position,x,Category()); + if(res!=x)return res; + else{ + BOOST_TRY{ + res=static_cast(Super::insert_(v,position,x)); + if(res!=x){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + } + return res; + } + BOOST_CATCH(...){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + } + + void erase_(node_type* x) + { + Super::erase_(x); + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + } + + void swap_(ordered_index& x) + { + std::swap(key,x.key); + std::swap(comp,x.comp); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_super::swap(x); +#endif + + Super::swap_(x); + } + + bool replace_(value_param_type v,node_type* x) + { + if(!comp(key(v),key(x->value))&& + !comp(key(x->value),key(v))){ + return Super::replace_(v,x); + } + + node_type* prior=x; + node_type::decrement(prior); + node_type* next=x; + node_type::increment(next); + + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + + BOOST_TRY{ + if(link2(key(v),x,Category())!=x){ + ordered_index_node_impl::restore( + x->impl(),prior->impl(),next->impl(),header()->impl()); + return false; + } + + BOOST_TRY{ + if(!Super::replace_(v,x)){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + ordered_index_node_impl::restore( + x->impl(),prior->impl(),next->impl(),header()->impl()); + return false; + } + else return true; + } + BOOST_CATCH(...){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + BOOST_CATCH(...){ + ordered_index_node_impl::restore( + x->impl(),prior->impl(),next->impl(),header()->impl()); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + + bool modify_(node_type* x) + { + bool b; + BOOST_TRY{ + b=in_place(x,Category()); + } + BOOST_CATCH(...){ + erase_(x); + BOOST_RETHROW; + } + BOOST_CATCH_END + if(!b){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + BOOST_TRY{ + if(link2(key(x->value),x,Category())!=x){ + Super::erase_(x); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + return false; + } + } + BOOST_CATCH(...){ + Super::erase_(x); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + + BOOST_RETHROW; + } + BOOST_CATCH_END + } + + BOOST_TRY{ + if(!Super::modify_(x)){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + + return false; + } + else return true; + } + BOOST_CATCH(...){ + ordered_index_node_impl::rebalance_for_erase( + x->impl(),header()->parent(),header()->left(),header()->right()); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + + BOOST_RETHROW; + } + BOOST_CATCH_END + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + /* invariant stuff */ + + bool invariant_()const + { + if(size()==0||begin()==end()){ + if(size()!=0||begin()!=end()|| + header()->left()!=header()->impl()|| + header()->right()!=header()->impl())return false; + } + else{ + if((size_type)std::distance(begin(),end())!=size())return false; + + std::size_t len=ordered_index_node_impl::black_count( + leftmost()->impl(),root()->impl()); + for(const_iterator it=begin();it!=end();++it){ + node_type* x=it.get_node(); + node_type* left_x=node_type::from_impl(x->left()); + node_type* right_x=node_type::from_impl(x->right()); + + if(x->color()==red){ + if((left_x&&left_x->color()==red)|| + (right_x&&right_x->color()==red))return false; + } + if(left_x&&comp(key(x->value),key(left_x->value)))return false; + if(right_x&&comp(key(right_x->value),key(x->value)))return false; + if(!left_x&&!right_x&& + ordered_index_node_impl::black_count( + x->impl(),root()->impl())!=len) + return false; + } + + if(leftmost()->impl()!= + ordered_index_node_impl::minimum(root()->impl())) + return false; + if(rightmost()->impl()!= + ordered_index_node_impl::maximum(root()->impl())) + return false; + } + + return Super::invariant_(); + } + + + /* This forwarding function eases things for the boost::mem_fn construct + * in BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT. Actually, + * final_check_invariant is already an inherited member function of + * ordered_index. + */ + void check_invariant_()const{this->final_check_invariant_();} +#endif + +private: + node_type* header()const{return this->final_header();} + node_type* root()const{return node_type::from_impl(header()->parent());} + node_type* leftmost()const{return node_type::from_impl(header()->left());} + node_type* rightmost()const{return node_type::from_impl(header()->right());} + + void empty_initialize() + { + header()->color()=red; + /* used to distinguish header() from root, in iterator.operator++ */ + + header()->parent()=0; + header()->left()=header()->impl(); + header()->right()=header()->impl(); + } + + node_type* link4(key_param_type k,node_type* x,node_type* y,node_type* z) + { + if(x!=0||y==header()||comp(k,key(y->value))){ + y->left()=z->impl(); /* also makes leftmost()=z when y==header() */ + if (y==header()){ + header()->parent()=z->impl(); + header()->right()=z->impl(); + } + else if(y==leftmost()){ + header()->left()=z->impl(); + /* maintain leftmost() pointing to min node */ + } + } + else{ + y->right()=z->impl(); + if(y==rightmost()){ + header()->right()=z->impl(); + /* maintain rightmost() pointing to max node */ + } + } + z->parent()=y->impl(); + z->left()=0; + z->right()=0; + ordered_index_node_impl::rebalance(z->impl(),header()->parent()); + return z; + } + + node_type* link2(key_param_type k,node_type* z,ordered_unique_tag) + { + node_type* y=header(); + node_type* x=root(); + bool c=true; + while(x){ + y=x; + c=comp(k,key(x->value)); + x=node_type::from_impl(c?x->left():x->right()); + } + iterator j=make_iterator(y); + if(c){ + if(j==begin())return link4(k,x,y,z); + else --j; + } + + if(comp(key(*j),k))return link4(k,x,y,z); + else return j.get_node(); + } + + node_type* link2(key_param_type k,node_type* z,ordered_non_unique_tag) + { + node_type* y=header(); + node_type* x=root(); + while (x){ + y=x; + x=node_type::from_impl(comp(k,key(x->value))?x->left():x->right()); + } + return link4(k,x,y,z); + } + + node_type* link3( + key_param_type k,node_type* position,node_type* z,ordered_unique_tag) + { + if(position->impl()==header()->left()){ + if(size()>0&&comp(k,key(position->value))){ + return link4(k,position,position,z); + } + else return link2(k,z,ordered_unique_tag()); + } + else if(position==header()){ + if(comp(key(rightmost()->value),k)){ + return link4(k,0,rightmost(),z); + } + else return link2(k,z,ordered_unique_tag()); + } + else{ + node_type* before=position; + node_type::decrement(before); + if(comp(key(before->value),k)&&comp(k,key(position->value))){ + if(before->right()==0)return link4(k,0,before,z); + else return link4(k,position,position,z); + } + else return link2(k,z,ordered_unique_tag()); + } + } + + node_type* link3( + key_param_type k,node_type* position,node_type* z,ordered_non_unique_tag) + { + if(position->impl()==header()->left()){ + if(size()>0&&!comp(key(position->value),k)){ + return link4(k,position,position,z); + } + else return link2(k,z,ordered_non_unique_tag()); + } + else if(position==header()){ + if(!comp(k,key(rightmost()->value))){ + return link4(k,0,rightmost(),z); + } + else return link2(k,z,ordered_non_unique_tag()); + } + else{ + node_type* before=position; + node_type::decrement(before); + if (!comp(k,key(before->value))&&!comp(key(position->value),k)){ + if(before->right()==0)return link4(k,0,before,z); + else return link4(k,position,position,z); + } + else return link2(k,z,ordered_non_unique_tag()); + } + } + + bool in_place(node_type* x,ordered_unique_tag) + { + node_type* y=x; + node_type::decrement(y); + if(y!=header()&&!comp(key(y->value),key(x->value)))return false; + + y=x; + node_type::increment(y); + return y==header()||comp(key(x->value),key(y->value)); + } + + bool in_place(node_type* x,ordered_non_unique_tag) + { + node_type* y=x; + node_type::decrement(y); + if(y!=header()&&comp(key(x->value),key(y->value)))return false; + + y=x; + node_type::increment(y); + return y==header()||!comp(key(y->value),key(x->value)); + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + void detach_iterators(node_type* x) + { + iterator it=make_iterator(x); + safe_mode::detach_equivalent_iterators(it); + } +#endif + + template + const_iterator lower_range(LowerBounder lower)const + { + node_type* y=header(); + node_type* z=root(); + + while(z){ + if(lower(key(z->value))){ + y=z; + z=node_type::from_impl(z->left()); + } + else z=node_type::from_impl(z->right()); + } + + return make_iterator(y); + } + + const_iterator lower_range(unbounded_type)const + { + return begin(); + } + + template + const_iterator upper_range(UpperBounder upper)const + { + node_type* y=header(); + node_type* z=root(); + + while(z){ + if(!upper(key(z->value))){ + y=z; + z=node_type::from_impl(z->left()); + } + else z=node_type::from_impl(z->right()); + } + + return make_iterator(y); + } + + const_iterator upper_range(unbounded_type)const + { + return end(); + } + + key_from_value key; + key_compare comp; +}; + +/* comparison */ + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator==( + const ordered_index& x, + const ordered_index& y) +{ + return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin()); +} + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator<( + const ordered_index& x, + const ordered_index& y) +{ + return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end()); +} + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator!=( + const ordered_index& x, + const ordered_index& y) +{ + return !(x==y); +} + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator>( + const ordered_index& x, + const ordered_index& y) +{ + return y +bool operator>=( + const ordered_index& x, + const ordered_index& y) +{ + return !(x +bool operator<=( + const ordered_index& x, + const ordered_index& y) +{ + return !(x>y); +} + +/* specialized algorithms */ + +template< + typename KeyFromValue,typename Compare, + typename Super,typename TagList,typename Category +> +void swap( + ordered_index& x, + ordered_index& y) +{ + x.swap(y); +} + +} /* namespace multi_index::detail */ + +/* ordered_index specifiers */ + +template +struct ordered_unique +{ + typedef typename detail::ordered_index_args< + Arg1,Arg2,Arg3> index_args; + typedef typename index_args::tag_list_type tag_list_type; + typedef typename index_args::key_from_value_type key_from_value_type; + typedef typename index_args::compare_type compare_type; + + template + struct node_class + { + typedef detail::ordered_index_node type; + }; + + template + struct index_class + { + typedef detail::ordered_index< + key_from_value_type,compare_type, + Super,tag_list_type,detail::ordered_unique_tag> type; + }; +}; + +template +struct ordered_non_unique +{ + typedef detail::ordered_index_args< + Arg1,Arg2,Arg3> index_args; + typedef typename index_args::tag_list_type tag_list_type; + typedef typename index_args::key_from_value_type key_from_value_type; + typedef typename index_args::compare_type compare_type; + + template + struct node_class + { + typedef detail::ordered_index_node type; + }; + + template + struct index_class + { + typedef detail::ordered_index< + key_from_value_type,compare_type, + Super,tag_list_type,detail::ordered_non_unique_tag> type; + }; +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#undef BOOST_MULTI_INDEX_ORD_INDEX_CHECK_INVARIANT + +#endif diff --git a/include/boost/multi_index/ordered_index_fwd.hpp b/include/boost/multi_index/ordered_index_fwd.hpp new file mode 100644 index 0000000..1856af8 --- /dev/null +++ b/include/boost/multi_index/ordered_index_fwd.hpp @@ -0,0 +1,112 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_ORDERED_INDEX_FWD_HPP +#define BOOST_MULTI_INDEX_ORDERED_INDEX_FWD_HPP + +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template< + typename KeyFromValue,typename Compare, + typename Super,typename TagList,typename Category +> +class index; + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator==( + const index& x, + const index& y); + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator<( + const index& x, + const index& y); + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator!=( + const index& x, + const index& y); + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator>( + const index& x, + const index& y); + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator>=( + const index& x, + const index& y); + +template< + typename KeyFromValue1,typename Compare1, + typename Super1,typename TagList1,typename Category1, + typename KeyFromValue2,typename Compare2, + typename Super2,typename TagList2,typename Category2 +> +bool operator<=( + const index& x, + const index& y); + +template< + typename KeyFromValue,typename Compare, + typename Super,typename TagList,typename Category +> +void swap( + index& x, + index& y); + +} /* namespace multi_index::detail */ + +/* index specifiers */ + +template< + typename Arg1,typename Arg2=detail::null_arg,typename Arg3=detail::null_arg +> +struct ordered_unique; + +template< + typename Arg1,typename Arg2=detail::null_arg,typename Arg3=detail::null_arg +> +struct ordered_non_unique; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/safe_mode_errors.hpp b/include/boost/multi_index/safe_mode_errors.hpp new file mode 100644 index 0000000..54bbdde --- /dev/null +++ b/include/boost/multi_index/safe_mode_errors.hpp @@ -0,0 +1,43 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_SAFE_MODE_ERRORS_HPP +#define BOOST_MULTI_INDEX_SAFE_MODE_ERRORS_HPP + +namespace boost{ + +namespace multi_index{ + +namespace safe_mode{ + +/* Error codes for Boost.MultiIndex safe mode. These go in a separate + * header so that the user can include it when redefining + * BOOST_MULTI_INDEX_SAFE_MODE_ASSERT prior to the inclusion of + * any other header of Boost.MultiIndex. + */ + +enum error_code +{ + invalid_iterator=0, + not_dereferenceable_iterator, + not_incrementable_iterator, + not_decrementable_iterator, + not_owner, + not_same_owner, + invalid_range, + inside_range, + same_container +}; + +} /* namespace multi_index::safe_mode */ + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/sequenced_index.hpp b/include/boost/multi_index/sequenced_index.hpp new file mode 100644 index 0000000..7764434 --- /dev/null +++ b/include/boost/multi_index/sequenced_index.hpp @@ -0,0 +1,751 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_SEQUENCED_INDEX_HPP +#define BOOST_MULTI_INDEX_SEQUENCED_INDEX_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) +#define BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT \ + detail::scope_guard BOOST_JOIN(check_invariant_,__LINE__)= \ + detail::make_obj_guard(*this,&sequenced_index::check_invariant_); \ + BOOST_JOIN(check_invariant_,__LINE__).touch(); +#else +#define BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT +#endif + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +/* sequenced_index adds a layer of sequenced indexing to a given Super */ + +template + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) +class sequenced_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super, + public index_proxy > +#else +class sequenced_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super, + public safe_container > +#endif +#else +class sequenced_index: + BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS Super +#endif + +{ +protected: + typedef sequenced_index_node node_type; + +public: + /* types */ + + typedef typename node_type::value_type value_type; + typedef tuples::null_type ctor_args; + typedef typename Super::final_allocator_type allocator_type; + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#else + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#endif +#else + typedef index_iterator iterator; + typedef index_iterator const_iterator; +#endif + + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef typename allocator_type::pointer pointer; + typedef typename allocator_type::const_pointer const_pointer; + typedef typename + boost::reverse_iterator reverse_iterator; + typedef typename + boost::reverse_iterator const_reverse_iterator; + typedef typename TagList::type tag_list; + +protected: + typedef typename Super::final_node_type final_node_type; + typedef tuples::cons< + ctor_args, + typename Super::ctor_args_list> ctor_args_list; + typedef typename mpl::push_front< + typename Super::index_type_list, + sequenced_index>::type index_type_list; + typedef typename mpl::push_front< + typename Super::iterator_type_list, + iterator>::type iterator_type_list; + typedef typename mpl::push_front< + typename Super::const_iterator_type_list, + const_iterator>::type const_iterator_type_list; + typedef typename Super::copy_map_type copy_map_type; + +private: +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + typedef index_proxy > safe_super; +#else + typedef safe_container safe_super; +#endif +#endif + + typedef typename call_traits::param_type value_param_type; + +public: + + /* construct/copy/destroy + * Default and copy ctors are in the protected section as indices are + * not supposed to be created on their own. No range ctor either. + */ + + sequenced_index& operator=( + const sequenced_index& x) + { + this->final()=x.final(); + return *this; + } + + template + void assign(InputIterator first,InputIterator last) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + clear(); + for(;first!=last;++first)push_back(*first); + } + + void assign(size_type n,value_param_type value) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + clear(); + for(size_type i=0;ifinal().get_allocator(); + } + + /* iterators */ + + iterator begin() + {return make_iterator(node_type::from_impl(header()->next()));} + const_iterator begin()const + {return make_iterator(node_type::from_impl(header()->next()));} + iterator end(){return make_iterator(header());} + const_iterator end()const{return make_iterator(header());} + reverse_iterator rbegin(){return make_reverse_iterator(end());} + const_reverse_iterator rbegin()const{return make_reverse_iterator(end());} + reverse_iterator rend(){return make_reverse_iterator(begin());} + const_reverse_iterator rend()const{return make_reverse_iterator(begin());} + + /* capacity */ + + bool empty()const{return this->final_empty_();} + size_type size()const{return this->final_size_();} + size_type max_size()const{return this->final_max_size_();} + + void resize(size_type n,value_param_type x=value_type()) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + if(n>size())insert(end(),n-size(),x); + else if(n push_front(value_param_type x) + {return insert(begin(),x);} + void pop_front(){erase(begin());} + std::pair push_back(value_param_type x) + {return insert(end(),x);} + void pop_back(){erase(--end());} + + std::pair insert(iterator position,value_param_type x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + std::pair p=this->final_insert_(x); + /* "this->" not required by std, but CW9.2 seems to need it */ + + if(p.second)relink(position.get_node(),p.first); + return std::pair(make_iterator(p.first),p.second); + } + + void insert(iterator position,size_type n,value_param_type x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + for(size_type i=0;i + void insert(iterator position,InputIterator first,InputIterator last) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + for(;first!=last;++first)insert(position,*first); + } + + void erase(iterator position) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + /* MSVC++ 6.0 optimizer on safe mode code chokes if this + * this is not added. Left it for all compilers as it does no + * harm. + */ + + position.detach(); +#endif + + final_erase_(static_cast(position.get_node())); + } + + void erase(iterator first,iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,*this); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,*this); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + while(first!=last){ + erase(first++); + } + } + + bool replace(iterator position,value_param_type x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + return final_replace_( + x,static_cast(position.get_node())); + } + + template + bool modify(iterator position,Modifier mod) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + /* MSVC++ 6.0 optimizer on safe mode code chokes if this + * this is not added. Left it for all compilers as it does no + * harm. + */ + + position.detach(); +#endif + + return final_modify_( + mod,static_cast(position.get_node())); + } + + void swap(sequenced_index& x) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + final_swap_(x.final()); + } + + void clear() + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + erase(begin(),end()); + } + + /* list operations */ + + void splice(iterator position,sequenced_index& x) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_CHECK_DIFFERENT_CONTAINER(*this,x); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + iterator first=x.begin(),last=x.end(); + while(first!=last){ + if(insert(position,*first).second)x.erase(first++); + else ++first; + } + } + + void splice(iterator position,sequenced_index& x,iterator i) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,x); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + if(x==*this){ + if(position!=i)relink(position.get_node(),i.get_node()); + } + else{ + if(insert(position,*i).second){ + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + /* MSVC++ 6.0 optimizer has a hard time with safe mode, and the following + * workaround is needed. Left it for all compilers as it does no + * harm. + */ + i.detach(); + x.erase(x.make_iterator(i.get_node())); +#else + x.erase(i); +#endif + + } + } + } + + void splice( + iterator position,sequenced_index& x, + iterator first,iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,x); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,x); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + if(x==*this){ + BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last); + if(position!=last)relink( + position.get_node(),first.get_node(),last.get_node()); + } + else{ + while(first!=last){ + if(insert(position,*first).second)x.erase(first++); + else ++first; + } + } + } + + void remove(value_param_type value) + { + sequenced_index_remove( + *this,std::bind2nd(std::equal_to(),value)); + } + + template + void remove_if(Predicate pred) + { + sequenced_index_remove(*this,pred); + } + + void unique() + { + sequenced_index_unique(*this,std::equal_to()); + } + + template + void unique(BinaryPredicate binary_pred) + { + sequenced_index_unique(*this,binary_pred); + } + + void merge(sequenced_index& x) + { + sequenced_index_merge(*this,x,std::less()); + } + + template + void merge(sequenced_index& x,Compare comp) + { + sequenced_index_merge(*this,x,comp); + } + + void sort() + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + sequenced_index_sort(header(),std::less()); + } + + template + void sort(Compare comp) + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + sequenced_index_sort(header(),comp); + } + + void reverse() + { + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + sequenced_index_node_impl::reverse(header()->impl()); + } + + /* relocate operations */ + + void relocate(iterator position,iterator i) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_DEREFERENCEABLE_ITERATOR(i); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(i,*this); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + if(position!=i)relink(position.get_node(),i.get_node()); + } + + void relocate(iterator position,iterator first,iterator last) + { + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(position); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(position,*this); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(first); + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(last); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(first,*this); + BOOST_MULTI_INDEX_CHECK_IS_OWNER(last,*this); + BOOST_MULTI_INDEX_CHECK_VALID_RANGE(first,last); + BOOST_MULTI_INDEX_CHECK_OUTSIDE_RANGE(position,first,last); + BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT; + if(position!=last)relink( + position.get_node(),first.get_node(),last.get_node()); + } + +BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: + sequenced_index(const ctor_args_list& args_list,const allocator_type& al): + Super(args_list.get_tail(),al) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)&&\ + BOOST_WORKAROUND(BOOST_MSVC,<1300) + ,safe_super(final_header()) +#endif + + { + header()->prior()=header()->next()=header()->impl(); + } + + sequenced_index(const sequenced_index& x): + Super(x) + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE)&&\ + BOOST_WORKAROUND(BOOST_MSVC,<1300) + ,safe_super(final_header()) +#endif + + { + /* The actual copying takes place in subsequent call to copy_(). + */ + } + + ~sequenced_index() + { + /* the container is guaranteed to be empty by now */ + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + iterator make_iterator(node_type* node){return iterator(node,this);} + const_iterator make_iterator(node_type* node)const + {return const_iterator(node,const_cast(this));} +#else + iterator make_iterator(node_type* node){return iterator(node);} + const_iterator make_iterator(node_type* node)const + {return const_iterator(node);} +#endif + + void copy_(const sequenced_index& x,const copy_map_type& map) + { + node_type* org=x.header(); + node_type* cpy=header(); + do{ + node_type* next_org=node_type::from_impl(org->next()); + node_type* next_cpy=map.find(static_cast(next_org)); + cpy->next()=next_cpy->impl(); + next_cpy->prior()=cpy->impl(); + org=next_org; + cpy=next_cpy; + }while(org!=x.header()); + + Super::copy_(x,map); + } + + node_type* insert_(value_param_type v,node_type* x) + { + node_type* res=static_cast(Super::insert_(v,x)); + if(res==x)link(x); + return res; + } + + node_type* insert_(value_param_type v,node_type* position,node_type* x) + { + node_type* res=static_cast(Super::insert_(v,position,x)); + if(res==x)link(x); + return res; + } + + void erase_(node_type* x) + { + unlink(x); + Super::erase_(x); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + } + + void swap_(sequenced_index& x) + { +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + safe_super::swap(x); +#endif + + Super::swap_(x); + } + + bool replace_(value_param_type v,node_type* x) + { + return Super::replace_(v,x); + } + + bool modify_(node_type* x) + { + BOOST_TRY{ + if(!Super::modify_(x)){ + unlink(x); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + + return false; + } + else return true; + } + BOOST_CATCH(...){ + unlink(x); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + detach_iterators(x); +#endif + + BOOST_RETHROW; + } + BOOST_CATCH_END + + return true; + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + /* invariant stuff */ + + bool invariant_()const + { + if(size()==0||begin()==end()){ + if(size()!=0||begin()!=end()|| + header()->next()!=header()->impl()|| + header()->prior()!=header()->impl())return false; + } + else{ + size_type s=0; + for(const_iterator it=begin();it!=end();++it,++s){ + if(it.get_node()->next()->prior()!=it.get_node()->impl())return false; + if(it.get_node()->prior()->next()!=it.get_node()->impl())return false; + } + if(s!=size())return false; + } + + return Super::invariant_(); + } + + /* This forwarding function eases things for the boost::mem_fn construct + * in BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT. Actually, + * final_check_invariant is already an inherited member function of index. + */ + void check_invariant_()const{this->final_check_invariant_();} +#endif + +private: + node_type* header()const{return this->final_header();} + + void link(node_type* x) + { + sequenced_index_node_impl::link(x->impl(),header()->impl()); + }; + + static void unlink(node_type* x) + { + sequenced_index_node_impl::unlink(x->impl()); + } + + static void relink(node_type* position,node_type* x) + { + sequenced_index_node_impl::relink(position->impl(),x->impl()); + } + + static void relink(node_type* position,node_type* first,node_type* last) + { + sequenced_index_node_impl::relink( + position->impl(),first->impl(),last->impl()); + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + void detach_iterators(node_type* x) + { + iterator it=make_iterator(x); + safe_mode::detach_equivalent_iterators(it); + } +#endif +}; + +/* comparison */ + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator==( + const sequenced_index& x, + const sequenced_index& y) +{ + return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin()); +} + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator<( + const sequenced_index& x, + const sequenced_index& y) +{ + return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end()); +} + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator!=( + const sequenced_index& x, + const sequenced_index& y) +{ + return !(x==y); +} + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator>( + const sequenced_index& x, + const sequenced_index& y) +{ + return y +bool operator>=( + const sequenced_index& x, + const sequenced_index& y) +{ + return !(x +bool operator<=( + const sequenced_index& x, + const sequenced_index& y) +{ + return !(x>y); +} + +/* specialized algorithms */ + +template +void swap( + sequenced_index& x, + sequenced_index& y) +{ + x.swap(y); +} + +} /* namespace multi_index::detail */ + +/* sequenced index specifier */ + +template +struct sequenced +{ + BOOST_STATIC_ASSERT(detail::is_tag::value); + + template + struct node_class + { + typedef detail::sequenced_index_node type; + }; + + template + struct index_class + { + typedef detail::sequenced_index type; + }; +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#undef BOOST_MULTI_INDEX_SEQ_INDEX_CHECK_INVARIANT + +#endif diff --git a/include/boost/multi_index/sequenced_index_fwd.hpp b/include/boost/multi_index/sequenced_index_fwd.hpp new file mode 100644 index 0000000..baee842 --- /dev/null +++ b/include/boost/multi_index/sequenced_index_fwd.hpp @@ -0,0 +1,87 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_SEQUENCED_INDEX_FWD_HPP +#define BOOST_MULTI_INDEX_SEQUENCED_INDEX_FWD_HPP + +#include + +namespace boost{ + +namespace multi_index{ + +namespace detail{ + +template +class sequenced_index; + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator==( + const sequenced_index& x, + const sequenced_index& y); + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator<( + const sequenced_index& x, + const sequenced_index& y); + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator!=( + const sequenced_index& x, + const sequenced_index& y); + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator>( + const sequenced_index& x, + const sequenced_index& y); + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator>=( + const sequenced_index& x, + const sequenced_index& y); + +template< + typename Super1,typename TagList1, + typename Super2,typename TagList2 +> +bool operator<=( + const sequenced_index& x, + const sequenced_index& y); + +template +void swap( + sequenced_index& x, + sequenced_index& y); + +} /* namespace multi_index::detail */ + +/* index specifiers */ + +template > +struct sequenced; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/multi_index/tag.hpp b/include/boost/multi_index/tag.hpp new file mode 100644 index 0000000..f714a76 --- /dev/null +++ b/include/boost/multi_index/tag.hpp @@ -0,0 +1,79 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_TAG_HPP +#define BOOST_MULTI_INDEX_TAG_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include + +/* An alias to mpl::vector used to hide MPL from the user. + * tag contains types used as tag names for indices in get() functions. + */ + +/* This user_definable macro limits the number of elements of a tag; + * useful for shortening resulting symbol names (MSVC++ 6.0, for instance, + * has problems coping with very long symbol names.) + */ + +#if !defined(BOOST_MULTI_INDEX_LIMIT_TAG_SIZE) +#if defined(BOOST_MSVC)&&(BOOST_MSVC<1300) +#define BOOST_MULTI_INDEX_LIMIT_TAG_SIZE 3 +#else +#define BOOST_MULTI_INDEX_LIMIT_TAG_SIZE BOOST_MPL_LIMIT_VECTOR_SIZE +#endif +#endif + +#if BOOST_MULTI_INDEX_LIMIT_TAG_SIZE +struct is_tag +{ + BOOST_STATIC_CONSTANT(bool,value=(is_base_and_derived::value)); +}; + +} /* namespace multi_index::detail */ + +template< + BOOST_PP_ENUM_BINARY_PARAMS( + BOOST_MULTI_INDEX_TAG_SIZE, + typename T, + =mpl::void_ BOOST_PP_INTERCEPT) +> +struct tag:private detail::tag_marker +{ + typedef mpl::vector type; + + BOOST_STATIC_ASSERT(detail::no_duplicate_tags::value); +}; + +} /* namespace multi_index */ + +} /* namespace boost */ + +#undef BOOST_MULTI_INDEX_TAG_SIZE + +#endif diff --git a/include/boost/multi_index_container.hpp b/include/boost/multi_index_container.hpp new file mode 100644 index 0000000..59e9376 --- /dev/null +++ b/include/boost/multi_index_container.hpp @@ -0,0 +1,864 @@ +/* Multiply indexed container. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_HPP +#define BOOST_MULTI_INDEX_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) +#include +#define BOOST_MULTI_INDEX_CHECK_INVARIANT \ + detail::scope_guard BOOST_JOIN(check_invariant_,__LINE__)= \ + detail::make_obj_guard(*this,&multi_index_container::check_invariant_); \ + BOOST_JOIN(check_invariant_,__LINE__).touch(); +#else +#define BOOST_MULTI_INDEX_CHECK_INVARIANT +#endif + +namespace boost{ + +namespace multi_index{ + +template +class multi_index_container: + private base_from_member< + typename detail::allocator::rebind_to< + Allocator, + typename detail::multi_index_node_type< + Value,IndexSpecifierList,Allocator>::type + >::type>, + BOOST_MULTI_INDEX_PRIVATE_IF_MEMBER_TEMPLATE_FRIENDS detail::header_holder< + typename detail::multi_index_node_type< + Value,IndexSpecifierList,Allocator>::type, + multi_index_container >, + public detail::multi_index_base_type::type +{ +private: +#if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) + template friend class detail::index_base; + template friend class detail::header_holder; + template friend class detail::converter; +#endif + + typedef typename detail::multi_index_base_type< + Value,IndexSpecifierList,Allocator>::type super; + typedef base_from_member< + typename detail::allocator::rebind_to< + Allocator, + typename super::node_type + >::type> bfm_allocator; + typedef detail::header_holder< + typename super::node_type, + multi_index_container> bfm_header; + +public: + /* All types are inherited from super, a few are explicitly + * brought forward here to save us some typename's. + */ + +#if defined(BOOST_MSVC) + typedef + detail::default_constructible_tuple_cons< + typename super::ctor_args_list> ctor_args_list; +#else + typedef typename super::ctor_args_list ctor_args_list; +#endif + + typedef typename IndexSpecifierList::type index_specifier_type_list; + typedef typename super::index_type_list index_type_list; + typedef typename super::iterator_type_list iterator_type_list; + typedef typename super::const_iterator_type_list const_iterator_type_list; + typedef typename super::value_type value_type; + typedef typename super::final_allocator_type allocator_type; + typedef typename super::iterator iterator; + typedef typename super::const_iterator const_iterator; + + BOOST_STATIC_ASSERT( + detail::no_duplicate_tags_in_index_list::value); + + /* global project() needs to see this publicly */ + + typedef typename super::node_type node_type; + + /* construct/copy/destroy */ + + explicit multi_index_container( + const ctor_args_list& args_list=ctor_args_list(), + const allocator_type& al=allocator_type()): + bfm_allocator(al), + super(args_list,bfm_allocator::member), + node_count(0) + { + BOOST_MULTI_INDEX_CHECK_INVARIANT; + } + + template + multi_index_container( + InputIterator first,InputIterator last, + const ctor_args_list& args_list=ctor_args_list(), + const allocator_type& al=allocator_type()): + bfm_allocator(al), + super(args_list,bfm_allocator::member), + node_count(0) + { + BOOST_MULTI_INDEX_CHECK_INVARIANT; + BOOST_TRY{ + iterator hint=super::end(); + for(;first!=last;++first){ + hint=super::make_iterator(insert_(*first,hint.get_node()).first); + } + } + BOOST_CATCH(...){ + clean_up(); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + + multi_index_container( + const multi_index_container& x): + bfm_allocator(x.bfm_allocator::member), + super(x), + node_count(0) + { + BOOST_MULTI_INDEX_CHECK_INVARIANT; + copy_map_type map(bfm_allocator::member,x.size(),x.header(),header()); + for(const_iterator it=x.begin();it!=x.end();++it)map.clone(it.get_node()); + super::copy_(x,map); + map.release(); + node_count=x.size(); + } + + ~multi_index_container() + { + BOOST_MULTI_INDEX_CHECK_INVARIANT; + clean_up(); + } + + multi_index_container& operator=( + const multi_index_container& x) + { + BOOST_MULTI_INDEX_CHECK_INVARIANT; + multi_index_container tmp(x); + swap(tmp); + return *this; + } + + allocator_type get_allocator()const + { + return allocator_type(bfm_allocator::member); + } + + /* retrieval of indices by number */ + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) + template + struct nth_index + { + BOOST_STATIC_ASSERT(N>=0&&N::type::value); + typedef typename mpl::at_c::type type; + }; + + template + typename nth_index::type& get(BOOST_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) + { + BOOST_STATIC_ASSERT(N>=0&&N::type::value); + return *this; + } + + template + const typename nth_index::type& get( + BOOST_EXPLICIT_TEMPLATE_NON_TYPE(int,N))const + { + BOOST_STATIC_ASSERT(N>=0&&N::type::value); + return *this; + } +#endif + + /* retrieval of indices by tag */ + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) + template + struct index + { + typedef typename mpl::find_if< + index_type_list, + detail::has_tag + >::type iter; + + BOOST_STATIC_CONSTANT( + bool,index_found=!(is_same >::value)); + BOOST_STATIC_ASSERT(index_found); + + typedef typename iter::type type; + }; + + template + typename index::type& get(BOOST_EXPLICIT_TEMPLATE_TYPE(Tag)) + { + return *this; + } + + template + const typename index::type& get( + BOOST_EXPLICIT_TEMPLATE_TYPE(Tag))const + { + return *this; + } +#endif + + /* projection of iterators by number */ + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) + template + struct nth_index_iterator + { + typedef typename nth_index::type::iterator type; + }; + + template + struct nth_index_const_iterator + { + typedef typename nth_index::type::const_iterator type; + }; + + template + typename nth_index_iterator::type project( + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) + { + typedef typename nth_index::type index; + + BOOST_STATIC_ASSERT( + (mpl::contains::value)); + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + BOOST_MULTI_INDEX_CHECK_IS_OWNER( + it,static_cast(*this)); + + return index::make_iterator(static_cast(it.get_node())); + } + + template + typename nth_index_const_iterator::type project( + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N))const + { + typedef typename nth_index::type index; + + BOOST_STATIC_ASSERT(( + mpl::contains::value|| + mpl::contains::value)); + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + BOOST_MULTI_INDEX_CHECK_IS_OWNER( + it,static_cast(*this)); + return index::make_iterator(static_cast(it.get_node())); + } +#endif + + /* projection of iterators by tag */ + +#if !defined(BOOST_NO_MEMBER_TEMPLATES) + template + struct index_iterator + { + typedef typename index::type::iterator type; + }; + + template + struct index_const_iterator + { + typedef typename index::type::const_iterator type; + }; + + template + typename index_iterator::type project( + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag)) + { + typedef typename index::type index; + + BOOST_STATIC_ASSERT( + (mpl::contains::value)); + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + BOOST_MULTI_INDEX_CHECK_IS_OWNER( + it,static_cast(*this)); + return index::make_iterator(static_cast(it.get_node())); + } + + template + typename index_const_iterator::type project( + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag))const + { + typedef typename index::type index; + + BOOST_STATIC_ASSERT(( + mpl::contains::value|| + mpl::contains::value)); + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + BOOST_MULTI_INDEX_CHECK_IS_OWNER( + it,static_cast(*this)); + return index::make_iterator(static_cast(it.get_node())); + } +#endif + +BOOST_MULTI_INDEX_PROTECTED_IF_MEMBER_TEMPLATE_FRIENDS: + typedef typename super::copy_map_type copy_map_type; + + node_type* header()const + { + return bfm_header::member; + } + + node_type* allocate_node() + { + return detail::allocator::allocate(bfm_allocator::member,1); + } + + void deallocate_node(node_type* x) + { + detail::allocator::deallocate(bfm_allocator::member,x,1); + } + + bool empty_()const + { + return node_count==0; + } + + std::size_t size_()const + { + return node_count; + } + + std::size_t max_size_()const + { + return static_cast(-1); + } + + std::pair insert_(const Value& v) + { + node_type* x=allocate_node(); + BOOST_TRY{ + node_type* res=super::insert_(v,x); + if(res==x){ + ++node_count; + return std::pair(res,true); + } + else{ + deallocate_node(x); + return std::pair(res,false); + } + } + BOOST_CATCH(...){ + deallocate_node(x); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + + std::pair insert_(const Value& v,node_type* position) + { + node_type* x=allocate_node(); + BOOST_TRY{ + node_type* res=super::insert_(v,position,x); + if(res==x){ + ++node_count; + return std::pair(res,true); + } + else{ + deallocate_node(x); + return std::pair(res,false); + } + } + BOOST_CATCH(...){ + deallocate_node(x); + BOOST_RETHROW; + } + BOOST_CATCH_END + } + + void erase_(node_type* x) + { + super::erase_(x); + deallocate_node(x); + --node_count; + } + + void swap_(multi_index_container& x) + { + std::swap(bfm_header::member,x.bfm_header::member); + super::swap_(x); + std::swap(node_count,x.node_count); + } + + bool replace_(const Value& k,node_type* x) + { + return super::replace_(k,x); + } + + template + bool modify_(Modifier mod,node_type* x) + { + mod(const_cast(x->value)); + + BOOST_TRY{ + if(!super::modify_(x)){ + deallocate_node(x); + --node_count; + return false; + } + else return true; + } + BOOST_CATCH(...){ + deallocate_node(x); + --node_count; + BOOST_RETHROW; + } + BOOST_CATCH_END + } + +#if defined(BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING) + /* invariant stuff */ + + bool invariant_()const + { + return super::invariant_(); + } + + void check_invariant_()const + { + BOOST_MULTI_INDEX_INVARIANT_ASSERT(invariant_()); + } +#endif + +private: + void clean_up() + { + for(iterator it=super::begin();it!=super::end();)erase_(it++.get_node()); + } + + std::size_t node_count; +}; + +/* retrieval of indices by number */ + +template +struct nth_index +{ + BOOST_STATIC_CONSTANT( + int, + M=mpl::size::type::value); + BOOST_STATIC_ASSERT(N>=0&&N::type type; +}; + +template +typename nth_index< + multi_index_container,N>::type& +get( + multi_index_container& m + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename nth_index< + multi_index_container< + Value,IndexSpecifierList,Allocator>, + N + >::type index; + + BOOST_STATIC_ASSERT(N>=0&& + N< + mpl::size< + BOOST_DEDUCED_TYPENAME multi_index_type::index_type_list + >::type::value); + + return detail::converter::index(m); +} + +template +const typename nth_index< + multi_index_container,N>::type& +get( + const multi_index_container& m + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename nth_index< + multi_index_container< + Value,IndexSpecifierList,Allocator>, + N + >::type index; + + BOOST_STATIC_ASSERT(N>=0&& + N< + mpl::size< + BOOST_DEDUCED_TYPENAME multi_index_type::index_type_list + >::type::value); + + return detail::converter::index(m); +} + +/* retrieval of indices by tag */ + +template +struct index +{ + typedef typename MultiIndexContainer::index_type_list index_type_list; + + typedef typename mpl::find_if< + index_type_list, + detail::has_tag + >::type iter; + + BOOST_STATIC_CONSTANT( + bool,index_found=!(is_same >::value)); + BOOST_STATIC_ASSERT(index_found); + + typedef typename iter::type type; +}; + +template< + typename Tag,typename Value,typename IndexSpecifierList,typename Allocator +> +typename index< + multi_index_container,Tag>::type& +get( + multi_index_container& m + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename index< + multi_index_container< + Value,IndexSpecifierList,Allocator>, + Tag + >::type index; + + return detail::converter::index(m); +} + +template< + typename Tag,typename Value,typename IndexSpecifierList,typename Allocator +> +const typename index< + multi_index_container,Tag>::type& +get( + const multi_index_container& m + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename index< + multi_index_container< + Value,IndexSpecifierList,Allocator>, + Tag + >::type index; + + return detail::converter::index(m); +} + +/* projection of iterators by number */ + +template +struct nth_index_iterator +{ + typedef typename detail::prevent_eti< + nth_index, + typename nth_index::type>::type::iterator type; +}; + +template +struct nth_index_const_iterator +{ + typedef typename detail::prevent_eti< + nth_index, + typename nth_index::type + >::type::const_iterator type; +}; + +template< + int N,typename IteratorType, + typename Value,typename IndexSpecifierList,typename Allocator> +typename nth_index_iterator< + multi_index_container,N>::type +project( + multi_index_container& m, + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename nth_index::type index; + +#if !defined(BOOST_MSVC)||!(BOOST_MSVC<1300) /* this ain't work in MSVC++ 6.0 */ + BOOST_STATIC_ASSERT(( + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::iterator_type_list, + IteratorType>::value)); +#endif + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + typedef detail::converter< + multi_index_type, + BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; + BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); +#endif + + return detail::converter::iterator( + m,static_cast(it.get_node())); +} + +template< + int N,typename IteratorType, + typename Value,typename IndexSpecifierList,typename Allocator> +typename nth_index_const_iterator< + multi_index_container,N>::type +project( + const multi_index_container& m, + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_NON_TYPE(int,N)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename nth_index::type index; + +#if !defined(BOOST_MSVC)||!(BOOST_MSVC<1300) /* this ain't work in MSVC++ 6.0 */ + BOOST_STATIC_ASSERT(( + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::iterator_type_list, + IteratorType>::value|| + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::const_iterator_type_list, + IteratorType>::value)); +#endif + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + typedef detail::converter< + multi_index_type, + BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; + BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); +#endif + + return detail::converter::const_iterator( + m,static_cast(it.get_node())); +} + +/* projection of iterators by tag */ + +template +struct index_iterator +{ + typedef typename index::type::iterator type; +}; + +template +struct index_const_iterator +{ + typedef typename index::type::const_iterator type; +}; + +template< + typename Tag,typename IteratorType, + typename Value,typename IndexSpecifierList,typename Allocator> +typename index_iterator< + multi_index_container,Tag>::type +project( + multi_index_container& m, + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename index::type index; + +#if !defined(BOOST_MSVC)||!(BOOST_MSVC<1300) /* this ain't work in MSVC++ 6.0 */ + BOOST_STATIC_ASSERT(( + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::iterator_type_list, + IteratorType>::value)); +#endif + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + typedef detail::converter< + multi_index_type, + BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; + BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); +#endif + + return detail::converter::iterator( + m,static_cast(it.get_node())); +} + +template< + typename Tag,typename IteratorType, + typename Value,typename IndexSpecifierList,typename Allocator> +typename index_const_iterator< + multi_index_container,Tag>::type +project( + const multi_index_container& m, + IteratorType it + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(Tag)) +{ + typedef multi_index_container< + Value,IndexSpecifierList,Allocator> multi_index_type; + typedef typename index::type index; + +#if !defined(BOOST_MSVC)||!(BOOST_MSVC<1300) /* this ain't work in MSVC++ 6.0 */ + BOOST_STATIC_ASSERT(( + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::iterator_type_list, + IteratorType>::value|| + mpl::contains< + BOOST_DEDUCED_TYPENAME multi_index_type::const_iterator_type_list, + IteratorType>::value)); +#endif + + BOOST_MULTI_INDEX_CHECK_VALID_ITERATOR(it); + +#if defined(BOOST_MULTI_INDEX_ENABLE_SAFE_MODE) + typedef detail::converter< + multi_index_type, + BOOST_DEDUCED_TYPENAME IteratorType::container_type> converter; + BOOST_MULTI_INDEX_CHECK_IS_OWNER(it,converter::index(m)); +#endif + + return detail::converter::const_iterator( + m,static_cast(it.get_node())); +} + +/* Comparison. Simple forward to first index. */ + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator==( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)==get<0>(y); +} + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator<( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)(y); +} + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator!=( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)!=get<0>(y); +} + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator>( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)>get<0>(y); +} + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator>=( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)>=get<0>(y); +} + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator<=( + const multi_index_container& x, + const multi_index_container& y) +{ + return get<0>(x)<=get<0>(y); +} + +/* specialized algorithms */ + +template +void swap( + multi_index_container& x, + multi_index_container& y) +{ + x.swap(y); +} + +} /* namespace multi_index */ + +/* Associated global functions are promoted to namespace boost, except + * comparison operators and swap, which are meant to be Koenig looked-up. + */ + +using multi_index::get; +using multi_index::project; + +} /* namespace boost */ + +#undef BOOST_MULTI_INDEX_CHECK_INVARIANT + +#endif diff --git a/include/boost/multi_index_container_fwd.hpp b/include/boost/multi_index_container_fwd.hpp new file mode 100644 index 0000000..1f1fc04 --- /dev/null +++ b/include/boost/multi_index_container_fwd.hpp @@ -0,0 +1,117 @@ +/* Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_FWD_HPP +#define BOOST_MULTI_INDEX_FWD_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include + +namespace boost{ + +namespace multi_index{ + +/* Default value for IndexSpecifierList specifies a container + * equivalent to std::set. + */ + +template< + typename Value, + typename IndexSpecifierList=indexed_by > >, + typename Allocator=std::allocator > +class multi_index_container; + +template +struct nth_index; + +template +struct index; + +template +struct nth_index_iterator; + +template +struct nth_index_const_iterator; + +template +struct index_iterator; + +template +struct index_const_iterator; + +/* get and project functions not fwd declared due to problems + * with dependent typenames + */ + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator==( + const multi_index_container& x, + const multi_index_container& y); + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator<( + const multi_index_container& x, + const multi_index_container& y); + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator!=( + const multi_index_container& x, + const multi_index_container& y); + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator>( + const multi_index_container& x, + const multi_index_container& y); + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator>=( + const multi_index_container& x, + const multi_index_container& y); + +template< + typename Value1,typename IndexSpecifierList1,typename Allocator1, + typename Value2,typename IndexSpecifierList2,typename Allocator2 +> +bool operator<=( + const multi_index_container& x, + const multi_index_container& y); + +template +void swap( + multi_index_container& x, + multi_index_container& y); + +} /* namespace multi_index */ + +/* multi_index_container, being the main type of this library, is promoted to + * namespace boost. + */ + +using multi_index::multi_index_container; + +} /* namespace boost */ + +#endif diff --git a/index.html b/index.html new file mode 100644 index 0000000..664c02b --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + +Boost.MultiIndex Documentation + + + + +Automatic redirection failed, please go to +doc/index.html + + + diff --git a/perf/Jamfile b/perf/Jamfile new file mode 100644 index 0000000..c7fc945 --- /dev/null +++ b/perf/Jamfile @@ -0,0 +1,15 @@ +# Boost.MultiIndex performance tests Jamfile +# +# Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and distribution +# are subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/libs/multi_index for library home page. + +subproject libs/multi_index/perf ; + +exe test_perf + : test_perf.cpp + : $(BOOST_ROOT) + : release + ; diff --git a/perf/test_perf.cpp b/perf/test_perf.cpp new file mode 100644 index 0000000..3e3dadc --- /dev/null +++ b/perf/test_perf.cpp @@ -0,0 +1,556 @@ +/* Boost.MultiIndex performance test. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include /* keep it first to prevent nasty warns in MSVC */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace boost::multi_index; + +/* Measurement harness by Andrew Koenig, extracted from companion code to + * Stroustrup, B.: "Wrapping C++ Member Function Calls", The C++ Report, + * June 2000, Vol 12/No 6. + * Original code retrievable at: http://www.research.att.com/~bs/wrap_code.cpp + */ + +// How many clock units does it take to interrogate the clock? +static double clock_overhead() +{ + clock_t k = clock(), start, limit; + + // Wait for the clock to tick + do start = clock(); + while (start == k); + + // interrogate the clock until it has advanced at least a second + // (for reasonable accuracy) + limit = start + CLOCKS_PER_SEC; + + unsigned long r = 0; + while ((k = clock()) < limit) + ++r; + + return double(k - start) / r; +} + +// We'd like the odds to be factor:1 that the result is +// within percent% of the median +const int factor = 10; +const int percent = 20; + +// Measure a function (object) factor*2 times, +// appending the measurements to the second argument +template +void measure_aux(F f, vector& mv) +{ + static double ovhd = clock_overhead(); + + // Ensure we don't reallocate in mid-measurement + mv.reserve(mv.size() + factor*2); + + // Wait for the clock to tick + clock_t k = clock(); + clock_t start; + + do start = clock(); + while (start == k); + + // Do 2*factor measurements + for (int i = 2*factor; i; --i) { + unsigned long count = 0, limit = 1, tcount = 0; + const clock_t clocklimit = start + CLOCKS_PER_SEC/100; + clock_t t; + + do { + while (count < limit) { + f(); + ++count; + } + limit *= 2; + ++tcount; + } while ((t = clock()) < clocklimit); + + // Wait for the clock to tick again; + clock_t t2; + do ++tcount; + while ((t2 = clock()) == t); + + // Append the measurement to the vector + mv.push_back(((t2 - start) - (tcount * ovhd)) / count); + + // Establish a new starting point + start = t2; + } +} + +// Returns the number of clock units per iteration +// With odds of factor:1, the measurement is within percent% of +// the value returned, which is also the median of all measurements. +template +double measure(F f) +{ + vector mv; + + int n = 0; // iteration counter + do { + ++n; + + // Try 2*factor measurements + measure_aux(f, mv); + assert(mv.size() == 2*n*factor); + + // Compute the median. We know the size is even, so we cheat. + sort(mv.begin(), mv.end()); + double median = (mv[n*factor] + mv[n*factor-1])/2; + + // If the extrema are within threshold of the median, we're done + if (mv[n] > (median * (100-percent))/100 && + mv[mv.size() - n - 1] < (median * (100+percent))/100) + return median; + + } while (mv.size() < factor * 200); + + // Give up! + clog << "Help!\n\n"; + exit(1); +} + +/* dereferencing compare predicate */ + +template +struct it_compare +{ + bool operator()(const Iterator& x,const Iterator& y)const{return comp(*x,*y);} + +private: + Compare comp; +}; + +/* list_wrapper and multiset_wrapper adapt std::lists and std::multisets + * to make them conform to a set-like insert interface which test + * routines do assume. + */ + +template +struct list_wrapper:List +{ + typedef typename List::value_type value_type; + typedef typename List::iterator iterator; + + pair insert(const value_type& v) + { + List::push_back(v); + return pair(--end(),true); + } +}; + +template +struct multiset_wrapper:Multiset +{ + typedef typename Multiset::value_type value_type; + typedef typename Multiset::iterator iterator; + + pair insert(const value_type& v) + { + return pair(Multiset::insert(v),true); + } +}; + +/* space comsumption of manual simulations is determined by checking + * the node sizes of the containers involved. This cannot be done in a + * portable manner, so node_size has to be written on a per stdlibrary + * basis. Add your own versions if necessary. + */ + +#if defined(BOOST_DINKUMWARE_STDLIB) + +template +size_t node_size(const Container&) +{ + return sizeof(*Container().begin()._Mynode()); +} + +#elif defined(__GLIBCPP__) + +template +size_t node_size(const Container&) +{ + typedef typename Container::iterator::_Link_type node_ptr; + node_ptr p=0; + return sizeof(*p); +} + +template +size_t node_size(const list&) +{ + return sizeof(typename list::iterator::_Node); +} + +template +size_t node_size(const list_wrapper&) +{ + return sizeof(typename List::iterator::_Node); +} + +#else + +/* default version returns 0 by convention */ + +template +size_t node_size(const Container&) +{ + return 0; +} + +#endif + +/* mono_container runs the tested routine on multi_index and manual + * simulations comprised of one standard container. + * bi_container and tri_container run the equivalent routine for manual + * compositions of two and three standard containers, respectively. + */ + +template +struct mono_container +{ + mono_container(int n_):n(n_){} + + void operator()() + { + typedef typename Container::iterator iterator; + + Container c; + + for(int i=0;i +struct bi_container +{ + bi_container(int n_):n(n_){} + + void operator()() + { + typedef typename Container1::iterator iterator1; + typedef typename Container2::iterator iterator2; + + Container1 c1; + Container2 c2; + + for(int i=0;i +struct tri_container +{ + tri_container(int n_):n(n_){} + + void operator()() + { + typedef typename Container1::iterator iterator1; + typedef typename Container2::iterator iterator2; + typedef typename Container3::iterator iterator3; + + Container1 c1; + Container2 c2; + Container3 c3; + + for(int i=0;i +void run_tests( + const char* title + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedTest) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualTest)) +{ + cout< +void compare_structures( + const char* title + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType)) +{ + run_tests< + mono_container, + mono_container + >(title); +} + +template +void compare_structures2( + const char* title + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType1) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType2)) +{ + run_tests< + mono_container, + bi_container + >(title); +} + +template < + typename IndexedType, + typename ManualType1,typename ManualType2,typename ManualType3 +> +void compare_structures3( + const char* title + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(IndexedType) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType1) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType2) + BOOST_APPEND_EXPLICIT_TEMPLATE_TYPE(ManualType3)) +{ + run_tests< + mono_container, + tri_container + >(title); +} + +int main() +{ + { + /* 1 ordered index */ + + typedef multi_index_container indexed_t; + typedef set manual_t; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures( + "1 ordered index"); + } + { + /* 1 sequenced index */ + + typedef list_wrapper< + multi_index_container< + int, + indexed_by > + > + > indexed_t; + typedef list_wrapper > manual_t; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures( + "1 sequenced index"); + } + { + /* 2 ordered indices */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique >, + ordered_non_unique > + > + > indexed_t; + typedef set manual_t1; + typedef multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + manual_t1::key_compare + > + > manual_t2; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures2( + "2 ordered indices"); + } + { + /* 1 ordered index + 1 sequenced index */ + + typedef multi_index_container< + int, + indexed_by< + boost::multi_index::ordered_unique >, + sequenced<> + > + > indexed_t; + typedef list_wrapper< + list + > manual_t1; + typedef multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + std::less + > + > manual_t2; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures2( + "1 ordered index + 1 sequenced index"); + } + { + /* 3 ordered indices */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique >, + ordered_non_unique >, + ordered_non_unique > + > + > indexed_t; + typedef set manual_t1; + typedef multiset_wrapper< + multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + manual_t1::key_compare + > + > + > manual_t2; + typedef multiset< + manual_t2::iterator, + it_compare< + manual_t2::iterator, + manual_t2::key_compare + > + > manual_t3; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures3( + "3 ordered indices"); + } + { + /* 2 ordered indices + 1 sequenced index */ + + typedef multi_index_container< + int, + indexed_by< + ordered_unique >, + ordered_non_unique >, + sequenced<> + > + > indexed_t; + typedef list_wrapper< + list + > manual_t1; + typedef multiset_wrapper< + multiset< + manual_t1::iterator, + it_compare< + manual_t1::iterator, + std::less + > + > + > manual_t2; + typedef multiset< + manual_t2::iterator, + it_compare< + manual_t2::iterator, + manual_t2::key_compare + > + > manual_t3; + indexed_t dummy; /* MSVC++ 6.0 chokes if indexed_t is not instantiated */ + + compare_structures3( + "2 ordered indices + 1 sequenced index"); + } + + return 0; +} diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 0000000..d038ac6 --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,41 @@ +# Boost.MultiIndex tests Jamfile +# +# Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and distribution +# are subject to the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# +# See http://www.boost.org/libs/multi_index for library home page. + +subproject libs/multi_index/test ; + +# bring in rules for testing + +SEARCH on testing.jam = $(BOOST_BUILD_PATH) ; +include testing.jam ; + +# make tests run by default + +DEPENDS all : test ; + +{ + test-suite "multi_index" + : [ run test_basic_main.cpp test_basic.cpp ] + : [ run test_capacity_main.cpp test_capacity.cpp ] + : [ run test_comparison_main.cpp test_comparison.cpp ] + : [ run test_composite_key_main.cpp test_composite_key.cpp ] + : [ run test_conv_iterators_main.cpp test_conv_iterators.cpp ] + : [ run test_copy_assignment_main.cpp test_copy_assignment.cpp ] + : [ run test_iterators_main.cpp test_iterators.cpp ] + : [ run test_key_extractors_main.cpp test_key_extractors.cpp ] + : [ run test_list_ops_main.cpp test_list_ops.cpp ] + : [ run test_modifiers_main.cpp test_modifiers.cpp ] + : [ run test_mpl_ops_main.cpp test_mpl_ops.cpp ] + : [ run test_projection_main.cpp test_projection.cpp ] + : [ run test_range_main.cpp test_range.cpp ] + : [ run test_safe_mode_main.cpp test_safe_mode.cpp ] + : [ run test_set_ops_main.cpp test_set_ops.cpp ] + : [ run test_special_list_ops_main.cpp test_special_list_ops.cpp ] + : [ run test_special_set_ops_main.cpp test_special_set_ops.cpp ] + : [ run test_update_main.cpp test_update.cpp ] + ; +} diff --git a/test/employee.hpp b/test/employee.hpp new file mode 100644 index 0000000..8c57a74 --- /dev/null +++ b/test/employee.hpp @@ -0,0 +1,88 @@ +/* Used in Boost.MultiIndex tests. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_TEST_EMPLOYEE_HPP +#define BOOST_MULTI_INDEX_TEST_EMPLOYEE_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include +#include +#include +#include + +struct employee +{ + int id; + std::string name; + int age; + + employee(int id_,std::string name_,int age_):id(id_),name(name_),age(age_){} + + bool operator==(const employee& x)const + { + return id==x.id&&name==x.name&&age==x.age; + } + + bool operator<(const employee& x)const + { + return id >, + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + BOOST_MULTI_INDEX_MEMBER(employee,std::string,name)>, + boost::multi_index::ordered_non_unique< + boost::multi_index::tag, + BOOST_MULTI_INDEX_MEMBER(employee,int,age)>, + boost::multi_index::sequenced< + boost::multi_index::tag > > > + employee_set; + +#if defined(BOOST_NO_MEMBER_TEMPLATES) +typedef boost::multi_index::nth_index< + employee_set,1>::type employee_set_by_name; +#else +typedef employee_set::nth_index<1>::type employee_set_by_name; +#endif + +typedef boost::multi_index::index< + employee_set,age>::type employee_set_by_age; +typedef boost::multi_index::index< + employee_set,as_inserted>::type employee_set_as_inserted; + +#endif diff --git a/test/pair_of_ints.hpp b/test/pair_of_ints.hpp new file mode 100644 index 0000000..388e4bd --- /dev/null +++ b/test/pair_of_ints.hpp @@ -0,0 +1,34 @@ +/* Used in Boost.MultiIndex tests. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_TEST_PAIR_OF_INTS_HPP +#define BOOST_MULTI_INDEX_TEST_PAIR_OF_INTS_HPP + +#include /* keep it first to prevent nasty warns in MSVC */ +#include + +typedef std::pair pair_of_ints; + +inline void increment_first(pair_of_ints& p) +{ + ++p.first; +} + +inline void increment_second(pair_of_ints& p) +{ + ++p.second; +} + +inline void increment_int(int& x) +{ + ++x; +} + +#endif diff --git a/test/pre_multi_index.hpp b/test/pre_multi_index.hpp new file mode 100644 index 0000000..2ddb4f8 --- /dev/null +++ b/test/pre_multi_index.hpp @@ -0,0 +1,31 @@ +/* Used in Boost.MultiIndex tests. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#ifndef BOOST_MULTI_INDEX_TEST_PRE_MULTI_INDEX_HPP +#define BOOST_MULTI_INDEX_TEST_PRE_MULTI_INDEX_HPP + +#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING +#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE + +#include + +struct safe_mode_exception +{ + safe_mode_exception(boost::multi_index::safe_mode::error_code error_code_): + error_code(error_code_) + {} + + boost::multi_index::safe_mode::error_code error_code; +}; + +#define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) \ +if(!(expr)){throw safe_mode_exception(error_code);} + +#endif diff --git a/test/test_all_main.cpp b/test/test_all_main.cpp new file mode 100644 index 0000000..b1017ee --- /dev/null +++ b/test/test_all_main.cpp @@ -0,0 +1,53 @@ +/* Boost.MultiIndex test suite. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_basic.hpp" +#include "test_capacity.hpp" +#include "test_comparison.hpp" +#include "test_composite_key.hpp" +#include "test_conv_iterators.hpp" +#include "test_copy_assignment.hpp" +#include "test_iterators.hpp" +#include "test_key_extractors.hpp" +#include "test_list_ops.hpp" +#include "test_modifiers.hpp" +#include "test_mpl_ops.hpp" +#include "test_projection.hpp" +#include "test_range.hpp" +#include "test_safe_mode.hpp" +#include "test_set_ops.hpp" +#include "test_special_list_ops.hpp" +#include "test_special_set_ops.hpp" +#include "test_update.hpp" + +int test_main(int,char *[]) +{ + test_basic(); + test_capacity(); + test_comparison(); + test_composite_key(); + test_conv_iterators(); + test_copy_assignment(); + test_iterators(); + test_key_extractors(); + test_list_ops(); + test_modifiers(); + test_mpl_ops(); + test_projection(); + test_range(); + test_safe_mode(); + test_set_ops(); + test_special_list_ops(); + test_special_set_ops(); + test_update(); + + return 0; +} diff --git a/test/test_basic.cpp b/test/test_basic.cpp new file mode 100644 index 0000000..62ca600 --- /dev/null +++ b/test/test_basic.cpp @@ -0,0 +1,90 @@ +/* Boost.MultiIndex basic test. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_basic.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +struct less_by_employee_name +{ + bool operator()(const employee& e1,const employee& e2)const + { + return e1.name v; + +#if defined(BOOST_NO_MEMBER_TEMPLATES) + employee_set_by_name& i1=get(es); +#else + employee_set_by_name& i1=es.get(); +#endif + + const employee_set_by_age& i2=get<2>(es); + employee_set_as_inserted& i3=get<3>(es); + + es.insert(employee(0,"Joe",31)); + i1.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + i3.push_back(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + v.push_back(employee(0,"Joe",31)); + v.push_back(employee(1,"Robert",27)); + v.push_back(employee(2,"John",40)); + v.push_back(employee(3,"Albert",20)); + v.push_back(employee(4,"John",57)); + + { + /* by insertion order */ + + BOOST_CHECK(std::equal(i3.begin(),i3.end(),v.begin())); + } + + { + /* by id */ + + std::sort(v.begin(),v.end()); + BOOST_CHECK(std::equal(es.begin(),es.end(),v.begin())); + } + + { + /* by name */ + + std::sort(v.begin(),v.end(),less_by_employee_name()); + BOOST_CHECK(std::equal(i1.begin(),i1.end(),v.begin())); + } + + { + /* by age */ + + std::sort(v.begin(),v.end(),less_by_employee_age()); + BOOST_CHECK(std::equal(i2.begin(),i2.end(),v.begin())); + } +} diff --git a/test/test_basic.hpp b/test/test_basic.hpp new file mode 100644 index 0000000..059f91f --- /dev/null +++ b/test/test_basic.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex basic test. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_basic(); diff --git a/test/test_basic_main.cpp b/test/test_basic_main.cpp new file mode 100644 index 0000000..2b3a10e --- /dev/null +++ b/test/test_basic_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex basic test. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_basic.hpp" + +int test_main(int,char *[]) +{ + test_basic(); + return 0; +} diff --git a/test/test_capacity.cpp b/test/test_capacity.cpp new file mode 100644 index 0000000..9e0390a --- /dev/null +++ b/test/test_capacity.cpp @@ -0,0 +1,50 @@ +/* Boost.MultiIndex test for capacity memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_capacity.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_capacity() +{ + employee_set es; + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + BOOST_CHECK(!es.empty()); + BOOST_CHECK(es.size()==5); + + es.erase(es.begin()); + BOOST_CHECK(get(es).size()==4); + + es.erase(es.begin()); + BOOST_CHECK(!get(es).empty()); + BOOST_CHECK(get(es).size()==3); + + multi_index_container > > ss; + + ss.resize(10); + BOOST_CHECK(ss.size()==10); + + ss.resize(20); + BOOST_CHECK(ss.size()==20); + + ss.resize(5); + BOOST_CHECK(ss.size()==5); +} diff --git a/test/test_capacity.hpp b/test/test_capacity.hpp new file mode 100644 index 0000000..77ef410 --- /dev/null +++ b/test/test_capacity.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for capacity memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_capacity(); diff --git a/test/test_capacity_main.cpp b/test/test_capacity_main.cpp new file mode 100644 index 0000000..d81d4ce --- /dev/null +++ b/test/test_capacity_main.cpp @@ -0,0 +1,19 @@ +/* Boost.MultiIndex test for capacity memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_capacity.hpp" + +int test_main(int,char *[]) +{ + test_capacity(); + return 0; +} + diff --git a/test/test_comparison.cpp b/test/test_comparison.cpp new file mode 100644 index 0000000..ecbf886 --- /dev/null +++ b/test/test_comparison.cpp @@ -0,0 +1,84 @@ +/* Boost.MultiIndex test for comparison functions. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_comparison.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +template +struct lookup_list{ + typedef multi_index_container< + Value, + indexed_by< + sequenced<>, + ordered_non_unique > + > + > type; +}; + +void test_comparison() +{ + employee_set es; + employee_set_by_name& i1=get<1>(es); + employee_set_as_inserted& i3=get<3>(es); + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + employee_set es2; + employee_set_by_name& i12=get(es2); + employee_set_as_inserted& i32=get<3>(es2); + es2.insert(employee(0,"Joe",31)); + es2.insert(employee(1,"Robert",27)); + es2.insert(employee(2,"John",40)); + es2.insert(employee(3,"Albert",20)); + + BOOST_CHECK(es==es&&es<=es&&es>=es&& + i12==i12&&i12<=i12&&i12>=i12&& + i32==i32&&i32<=i32&&i32>=i32); + BOOST_CHECK(es!=es2&&es2es2&&!(es<=es2)&&!(es2>=es)); + BOOST_CHECK(i1!=i12&&i12i12&&!(i1<=i12)&&!(i12>=i1)); + BOOST_CHECK(i3!=i32&&i32i32&&!(i3<=i32)&&!(i32>=i3)); + + lookup_list::type l1; + lookup_list::type l2; + lookup_list::type l3; + + l1.push_back(3); + l1.push_back(4); + l1.push_back(5); + l1.push_back(1); + l1.push_back(2); + + l2.push_back(char(3)); + l2.push_back(char(4)); + l2.push_back(char(5)); + l2.push_back(char(1)); + l2.push_back(char(2)); + + l3.push_back(long(3)); + l3.push_back(long(4)); + l3.push_back(long(5)); + l3.push_back(long(1)); + + BOOST_CHECK(l1==l2&&l1<=l2&&l1>=l2); + BOOST_CHECK( + get<1>(l1)==get<1>(l2)&&get<1>(l1)<=get<1>(l2)&&get<1>(l1)>=get<1>(l2)); + BOOST_CHECK(l1!=l3&&l3l3); + BOOST_CHECK( + get<1>(l1)!=get<1>(l3)&&get<1>(l1)(l3)&&get<1>(l3)>get<1>(l1)); +} diff --git a/test/test_comparison.hpp b/test/test_comparison.hpp new file mode 100644 index 0000000..3e68dd6 --- /dev/null +++ b/test/test_comparison.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for comparison functions. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_comparison(); diff --git a/test/test_comparison_main.cpp b/test/test_comparison_main.cpp new file mode 100644 index 0000000..15b46bb --- /dev/null +++ b/test/test_comparison_main.cpp @@ -0,0 +1,19 @@ +/* Boost.MultiIndex test for comparison functions. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_comparison.hpp" + +int test_main(int,char *[]) +{ + test_comparison(); + return 0; +} + diff --git a/test/test_composite_key.cpp b/test/test_composite_key.cpp new file mode 100644 index 0000000..bab55d0 --- /dev/null +++ b/test/test_composite_key.cpp @@ -0,0 +1,448 @@ +/* Boost.MultiIndex test for composite_key. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_composite_key.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include +#include +#include +#include +#include + +using namespace boost::multi_index; +using namespace boost::tuples; + +template +struct is_composite_key_result +{ + typedef char yes; + struct no{char m[2];}; + + static no test(void *); + + template + static yes test(composite_key_result*); + + static T* make(); + + BOOST_STATIC_CONSTANT(bool,value=(sizeof(test(make()))==sizeof(yes))); +}; + +template +struct composite_key_result_length +{ + BOOST_STATIC_CONSTANT(int, + value=boost::tuples::length< + BOOST_DEDUCED_TYPENAME + CompositeKeyResult::composite_key_type::key_extractor_tuple + >::value); +}; + +template +struct composite_object_length +{ + typedef typename boost::mpl::if_c< + is_composite_key_result::value, + composite_key_result_length, + boost::tuples::length + >::type type; + + BOOST_STATIC_CONSTANT(int,value=type::value); +}; + +template +struct comparison_equal_length +{ + static bool is_less(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return (x< y) && !(y< x)&& + !(x==y) && !(y==x)&& + (x!=y) && (y!=x)&& + !(x> y) && (y> x)&& + !(x>=y) && (y>=x)&& + (x<=y) && !(y<=x)&& + + lt(x,y) && !lt(y,x)&& + !gt(x,y) && gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + std_lt(x,y) && !std_lt(y,x)&& + !std_gt(x,y) && std_gt(y,x) +#endif + ; + } + + static bool is_greater(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return !(x< y) && (y< x)&& + !(x==y) && !(y==x)&& + (x!=y) && (y!=x)&& + (x> y) && !(y> x)&& + (x>=y) && !(y>=x)&& + !(x<=y) && (y<=x)&& + + !lt(x,y) && lt(y,x)&& + gt(x,y) && !gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + !std_lt(x,y) && std_lt(y,x)&& + std_gt(x,y) && !std_gt(y,x) +#endif + ; + } + + static bool is_equiv(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return !(x< y) && !(y< x)&& + (x==y) && (y==x)&& + !(x!=y) && !(y!=x)&& + !(x> y) && !(y> x)&& + (x>=y) && (y>=x)&& + (x<=y) && (y<=x)&& + + !lt(x,y) && !lt(y,x)&& + !gt(x,y) && !gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + !std_lt(x,y) && !std_lt(y,x)&& + !std_gt(x,y) && !std_gt(y,x) +#endif + ; + } +}; + +template +struct comparison_different_length +{ + static bool is_less(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return (x< y) && !(y< x)&& + !(x> y) && (y> x)&& + !(x>=y) && (y>=x)&& + (x<=y) && !(y<=x)&& + + lt(x,y) && !lt(y,x)&& + !gt(x,y) && gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + std_lt(x,y) && !std_lt(y,x)&& + !std_gt(x,y) && std_gt(y,x) +#endif + ; + } + + static bool is_greater(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return !(x< y) && (y< x)&& + (x> y) && !(y> x)&& + (x>=y) && !(y>=x)&& + !(x<=y) && (y<=x)&& + + !lt(x,y) && lt(y,x)&& + gt(x,y) && !gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + !std_lt(x,y) && std_lt(y,x)&& + std_gt(x,y) && !std_gt(y,x) +#endif + ; + } + + static bool is_equiv(const CompositeKeyResult& x,const T2& y) + { + composite_key_result_less lt; + composite_key_result_greater gt; + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + std::less std_lt; + std::greater std_gt; +#endif + + return !(x< y) && !(y< x)&& + !(x> y) && !(y> x)&& + (x>=y) && (y>=x)&& + (x<=y) && (y<=x)&& + + !lt(x,y) && !lt(y,x)&& + !gt(x,y) && !gt(y,x) + +#if !defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + && + !std_lt(x,y) && !std_lt(y,x)&& + !std_gt(x,y) && !std_gt(y,x) +#endif + ; + } +}; + +template +struct comparison_helper: + boost::mpl::if_c< + composite_key_result_length::value== + composite_object_length::value, + comparison_equal_length, + comparison_different_length + >::type +{ +}; + +template +static bool is_less(const CompositeKeyResult& x,const T2& y) +{ + return comparison_helper::is_less(x,y); +} + +template +static bool is_greater(const CompositeKeyResult& x,const T2& y) +{ + return comparison_helper::is_greater(x,y); +} + +template +static bool is_equiv(const CompositeKeyResult& x,const T2& y) +{ + return comparison_helper::is_equiv(x,y); +} + +template +static bool is_less(const T1& x,const T2& y,const Compare& c) +{ + return c(x,y)&&!c(y,x); +} + +template +static bool is_greater(const T1& x,const T2& y,const Compare& c) +{ + return c(y,x)&&!c(x,y); +} + +template +static bool is_equiv(const T1& x,const T2& y,const Compare& c) +{ + return !c(x,y)&&!c(y,x); +} + +struct xyz +{ + xyz(int x_=0,int y_=0,int z_=0):x(x_),y(y_),z(z_){} + + int x; + int y; + int z; +}; + +void test_composite_key() +{ + typedef composite_key< + xyz, + BOOST_MULTI_INDEX_MEMBER(xyz,int,x), + BOOST_MULTI_INDEX_MEMBER(xyz,int,y), + BOOST_MULTI_INDEX_MEMBER(xyz,int,z) + > ckey_t1; + + typedef multi_index_container< + xyz, + indexed_by< + ordered_unique< + ckey_t1 +#if defined(BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) + ,composite_key_result_less +#endif + > + > + > indexed_t; + + indexed_t mc; + mc.insert(xyz(0,0,0)); + mc.insert(xyz(0,0,1)); + mc.insert(xyz(0,1,0)); + mc.insert(xyz(0,1,1)); + mc.insert(xyz(1,0,0)); + mc.insert(xyz(1,0,1)); + mc.insert(xyz(1,1,0)); + mc.insert(xyz(1,1,1)); + + BOOST_CHECK(mc.size()==8); + BOOST_CHECK( + std::distance( + mc.find(mc.key_extractor()(xyz(0,0,0))), + mc.find(mc.key_extractor()(xyz(1,0,0))))==4); + BOOST_CHECK( + std::distance( + mc.find(make_tuple(0,0,0)), + mc.find(make_tuple(1,0,0)))==4); + BOOST_CHECK( + std::distance( + mc.lower_bound(make_tuple(0,0)), + mc.upper_bound(make_tuple(1,0)))==6); + + ckey_t1 ck1; + ckey_t1 ck2(ck1); + ckey_t1 ck3( + boost::make_tuple( + BOOST_MULTI_INDEX_MEMBER(xyz,int,x)(), + BOOST_MULTI_INDEX_MEMBER(xyz,int,y)(), + BOOST_MULTI_INDEX_MEMBER(xyz,int,z)())); + ckey_t1 ck4(get<0>(ck1.key_extractors())); + + get<2>(ck4.key_extractors())= + get<2>(ck2.key_extractors()); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),ck2(xyz(0,0,0)))); + BOOST_CHECK(is_less (ck1(xyz(0,0,1)),ck2(xyz(0,1,0)))); + BOOST_CHECK(is_greater(ck1(xyz(0,0,1)),ck2(xyz(0,0,0)))); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0))); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(1))); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(-1))); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0))); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,1))); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,-1))); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0,0))); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,0,1))); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,0,-1))); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0,0,1))); + + typedef composite_key_result_less ckey_comp_t1; + + ckey_comp_t1 cp1; + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),ck2(xyz(0,0,0)),cp1)); + BOOST_CHECK(is_less (ck1(xyz(0,0,1)),ck2(xyz(0,1,0)),cp1)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,1)),ck2(xyz(0,0,0)),cp1)); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0),cp1)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(1),cp1)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(-1),cp1)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0),cp1)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,1),cp1)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,-1),cp1)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0,0),cp1)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,0,1),cp1)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,0,-1),cp1)); + + typedef composite_key_result_greater ckey_comp_t2; + + ckey_comp_t2 cp2; + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),ck2(xyz(0,0,0)),cp2)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,1)),ck2(xyz(0,1,0)),cp2)); + BOOST_CHECK(is_less (ck1(xyz(0,0,1)),ck2(xyz(0,0,0)),cp2)); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0),cp2)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(1),cp2)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(-1),cp2)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0),cp2)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,1),cp2)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,-1),cp2)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0,0),cp2)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,0,1),cp2)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,0,-1),cp2)); + + typedef composite_key_compare< + std::less, + std::greater, /* order reversed */ + std::less + > ckey_comp_t3; + + ckey_comp_t3 cp3; + ckey_comp_t3 cp4(cp3); + ckey_comp_t3 cp5( + boost::make_tuple( + std::less(), + std::greater(), + std::less())); + ckey_comp_t3 cp6(get<0>(cp3.key_comps())); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),ck2(xyz(0,0,0)),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,1)),ck2(xyz(0,1,0)),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,1)),ck2(xyz(0,0,0)),cp3)); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0),cp3)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(1),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(-1),cp3)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0),cp3)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,-1),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,1),cp3)); + BOOST_CHECK(is_equiv (ck1(xyz(0,0,0)),make_tuple(0,0,0),cp3)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),make_tuple(0,0,1),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),make_tuple(0,0,-1),cp3)); + + typedef composite_key< + xyz, + BOOST_MULTI_INDEX_MEMBER(xyz,int,y), /* members reversed */ + BOOST_MULTI_INDEX_MEMBER(xyz,int,x) + > ckey_t2; + + ckey_t2 ck5; + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,1)),ck5(xyz(0,0,0)))); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),ck5(xyz(-1,1,0)))); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),ck5(xyz(1,-1,0)))); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,1)),ck5(xyz(0,0,0)),cp1)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),ck5(xyz(-1,1,0)),cp1)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),ck5(xyz(1,-1,0)),cp1)); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,1)),ck5(xyz(0,0,0)),cp2)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),ck5(xyz(-1,1,0)),cp2)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),ck5(xyz(1,-1,0)),cp2)); + + BOOST_CHECK(is_equiv (ck1(xyz(0,0,1)),ck5(xyz(0,0,0)),cp3)); + BOOST_CHECK(is_less (ck1(xyz(0,0,0)),ck5(xyz(-1,1,0)),cp3)); + BOOST_CHECK(is_greater(ck1(xyz(0,0,0)),ck5(xyz(1,-1,0)),cp3)); +} diff --git a/test/test_composite_key.hpp b/test/test_composite_key.hpp new file mode 100644 index 0000000..d7e4f54 --- /dev/null +++ b/test/test_composite_key.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for composite_key. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_composite_key(); diff --git a/test/test_composite_key_main.cpp b/test/test_composite_key_main.cpp new file mode 100644 index 0000000..f803851 --- /dev/null +++ b/test/test_composite_key_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for composite_key. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_composite_key.hpp" + +int test_main(int,char *[]) +{ + test_composite_key(); + return 0; +} diff --git a/test/test_conv_iterators.cpp b/test/test_conv_iterators.cpp new file mode 100644 index 0000000..41a8805 --- /dev/null +++ b/test/test_conv_iterators.cpp @@ -0,0 +1,55 @@ +/* Boost.MultiIndex test for interconvertibilty between const and + * non-const iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_conv_iterators.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_conv_iterators() +{ + employee_set es; + es.insert(employee(2,"John",40)); + + { + const employee_set& ces=es; + employee_set::iterator it=es.find(employee(2,"John",40)); + employee_set::const_iterator it1=es.find(employee(2,"John",40)); + employee_set::const_iterator it2=ces.find(employee(2,"John",40)); + + BOOST_CHECK(it==it1&&it1==it2&&it2==it); + BOOST_CHECK(*it==*it1&&*it1==*it2&&*it2==*it); + } + { + employee_set_by_name& i1=get<1>(es); + const employee_set_by_name& ci1=get<1>(es); + employee_set_by_name::iterator it=i1.find("John"); + employee_set_by_name::const_iterator it1=i1.find("John"); + employee_set_by_name::const_iterator it2=ci1.find("John"); + + BOOST_CHECK(it==it1&&it1==it2&&it2==it); + BOOST_CHECK(*it==*it1&&*it1==*it2&&*it2==*it); + } + { + employee_set_as_inserted& i3=get<3>(es); + const employee_set_as_inserted& ci3=get<3>(es); + employee_set_as_inserted::iterator it=i3.begin(); + employee_set_as_inserted::const_iterator it1=i3.begin(); + employee_set_as_inserted::const_iterator it2=ci3.begin(); + + BOOST_CHECK(it==it1&&it1==it2&&it2==it); + BOOST_CHECK(*it==*it1&&*it1==*it2&&*it2==*it); + } +} diff --git a/test/test_conv_iterators.hpp b/test/test_conv_iterators.hpp new file mode 100644 index 0000000..3fd81f4 --- /dev/null +++ b/test/test_conv_iterators.hpp @@ -0,0 +1,12 @@ +/* Boost.MultiIndex test for interconvertibilty between const and + * non-const iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_conv_iterators(); diff --git a/test/test_conv_iterators_main.cpp b/test/test_conv_iterators_main.cpp new file mode 100644 index 0000000..1586482 --- /dev/null +++ b/test/test_conv_iterators_main.cpp @@ -0,0 +1,21 @@ +/* Boost.MultiIndex test for interconvertibilty between const and + * non-const iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_conv_iterators.hpp" + +int test_main(int,char *[]) +{ + test_conv_iterators(); + return 0; +} + + diff --git a/test/test_copy_assignment.cpp b/test/test_copy_assignment.cpp new file mode 100644 index 0000000..6628341 --- /dev/null +++ b/test/test_copy_assignment.cpp @@ -0,0 +1,91 @@ +/* Boost.MultiIndex test for copying and assignment. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_copy_assignment.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_copy_assignment() +{ + employee_set es; + employee_set es2(es); + + BOOST_CHECK(es2.empty()); + + es2.insert(employee(0,"Joe",31)); + es2.insert(employee(1,"Robert",27)); + es2.insert(employee(2,"John",40)); + es2.insert(employee(2,"Aristotle",2387)); + es2.insert(employee(3,"Albert",20)); + es2.insert(employee(4,"John",57)); + + employee_set es3(es2); + + BOOST_CHECK(es2==es3); + BOOST_CHECK(get<2>(es2)==get<2>(es3)); + BOOST_CHECK(get<3>(es2)==get<3>(es3)); + + employee_set es4; + employee_set_by_age& i2=get(es4); + i2=get<2>(es2); + + BOOST_CHECK(i2==get<2>(es2)); + + employee_set es5; + employee_set_as_inserted& i3=get(es5); + i3=get<3>(es2); + + BOOST_CHECK(i3==get<3>(es2)); + + std::list l; + l.push_back(employee(3,"Anna",31)); + l.push_back(employee(1,"Rachel",27)); + l.push_back(employee(2,"Agatha",40)); + +#if BOOST_WORKAROUND(BOOST_MSVC,<1300) + employee_set es6; + es6.insert(l.begin(),l.end()); +#else + employee_set es6(l.begin(),l.end()); +#endif + + l.sort(); + + BOOST_CHECK(es6.size()==l.size()&& + std::equal(es6.begin(),es6.end(),l.begin())); + + multi_index_container > > ss; + + int a[]={0,1,2,3,4,5}; + std::size_t sa=sizeof(a)/sizeof(a[0]); + + ss.assign(&a[0],&a[sa]); + + BOOST_CHECK(ss.size()==ss.size()&&std::equal(ss.begin(),ss.end(),&a[0])); + + ss.assign(&a[0],&a[sa]); + + BOOST_CHECK(ss.size()==ss.size()&&std::equal(ss.begin(),ss.end(),&a[0])); + + ss.assign((std::size_t)18,37); + BOOST_CHECK(ss.size()==18&&std::accumulate(ss.begin(),ss.end(),0)==666); + + ss.assign((std::size_t)12,167); + BOOST_CHECK(ss.size()==12&&std::accumulate(ss.begin(),ss.end(),0)==2004); +} diff --git a/test/test_copy_assignment.hpp b/test/test_copy_assignment.hpp new file mode 100644 index 0000000..d38240f --- /dev/null +++ b/test/test_copy_assignment.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for copying and assignment. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_copy_assignment(); diff --git a/test/test_copy_assignment_main.cpp b/test/test_copy_assignment_main.cpp new file mode 100644 index 0000000..cd4dbb8 --- /dev/null +++ b/test/test_copy_assignment_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for copying and assignment. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_copy_assignment.hpp" + +int test_main(int,char *[]) +{ + test_copy_assignment(); + return 0; +} diff --git a/test/test_iterators.cpp b/test/test_iterators.cpp new file mode 100644 index 0000000..088d75c --- /dev/null +++ b/test/test_iterators.cpp @@ -0,0 +1,87 @@ +/* Boost.MultiIndex test for iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_iterators.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_iterators() +{ + employee_set es; + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + { + int n=0; + for(employee_set::const_iterator it=es.begin();it!=es.end();++it){ + n+=it->id; + } + int m=0; + for(employee_set::reverse_iterator rit=es.rbegin();rit!=es.rend();++rit){ + m+=rit->id; + } + int p=0; + for(employee_set::const_iterator it2=es.end();it2!=es.begin();){ + --it2; + p+=it2->id; + } + int q=0; + for(employee_set::reverse_iterator rit2=es.rend();rit2!=es.rbegin();){ + --rit2; + q+=rit2->id; + } + + BOOST_CHECK(n==0+1+2+3+4&&n==m&&n==p&&n==q); + } + + { + int n=0; + employee_set_by_name& i1=get(es); + for(employee_set_by_name::iterator it=i1.begin();it!=i1.end();++it){ + n+=it->id; + } + int m=0; + const employee_set_by_age& i2=get<2>(es); + for(employee_set_by_age::const_reverse_iterator rit=i2.rbegin(); + rit!=i2.rend();++rit){ + m+=rit->id; + } + int p=0; + const employee_set_as_inserted& i3=get<3>(es); + for( + employee_set_as_inserted::const_reverse_iterator rit2=i3.rbegin(); + rit2!=i3.rend();++rit2){ + p+=rit2->id; + } + int q=0; + for(employee_set_by_name::iterator it2=i1.end();it2!=i1.begin();){ + --it2; + q+=it2->id; + } + int r=0; + for( + employee_set_as_inserted::const_iterator it3=i3.end(); + it3!=i3.begin();){ + --it3; + r+=it3->id; + } + + BOOST_CHECK(n==0+1+2+3+4&&n==m&&n==p&&n==q&&n==r); + } +} diff --git a/test/test_iterators.hpp b/test/test_iterators.hpp new file mode 100644 index 0000000..7e9d40c --- /dev/null +++ b/test/test_iterators.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_iterators(); diff --git a/test/test_iterators_main.cpp b/test/test_iterators_main.cpp new file mode 100644 index 0000000..d3a1dad --- /dev/null +++ b/test/test_iterators_main.cpp @@ -0,0 +1,19 @@ +/* Boost.MultiIndex test for iterators. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_iterators.hpp" + +int test_main(int,char *[]) +{ + test_iterators(); + return 0; +} + diff --git a/test/test_key_extractors.cpp b/test/test_key_extractors.cpp new file mode 100644 index 0000000..a7d187c --- /dev/null +++ b/test/test_key_extractors.cpp @@ -0,0 +1,215 @@ +/* Boost.MultiIndex test for key extractors. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_key_extractors.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include +#include +#include +#include +#include +#include + +using namespace boost::multi_index; +using namespace boost::tuples; + +struct test_class +{ + int int_member; + const int int_cmember; + + bool bool_mem_fun_const()const{return true;} + bool bool_mem_fun(){return false;} + + test_class(int i=0):int_member(i),int_cmember(i){} + test_class(int i,int j):int_member(i),int_cmember(j){} + + bool operator<(const test_class& x)const + { + if(int_member idn; +typedef identity cidn; +typedef BOOST_MULTI_INDEX_MEMBER(test_class,int,int_member) key_m; +typedef BOOST_MULTI_INDEX_MEMBER(test_class,const int,int_member) ckey_m; +typedef BOOST_MULTI_INDEX_MEMBER(test_class,const int,int_cmember) key_cm; +typedef BOOST_MULTI_INDEX_CONST_MEM_FUN( + test_class,bool,bool_mem_fun_const) key_cmf; +typedef BOOST_MULTI_INDEX_MEM_FUN(test_class,bool,bool_mem_fun) key_mf; +typedef composite_key< + test_class, + idn, + key_m, + key_cm, + key_cmf + > compkey; +typedef composite_key< + test_class, + cidn, + ckey_m + > ccompkey; +typedef composite_key< + boost::reference_wrapper, + key_mf + > ccompw_key; + +void test_key_extractors() +{ + idn id; + cidn cid; + key_m k_m; + ckey_m ck_m; + key_cm k_cm; + key_cmf k_cmf; + key_mf k_mf; + compkey cmpk; + ccompkey ccmpk; + ccompw_key ccmpk_w; + + test_class t; + const test_class& ctr=t; + + test_class* tp=&t; + const test_class* ctp=&t; + + test_class** tpp=&tp; + const test_class** ctpp=&ctp; + + std::auto_ptr tap(new test_class*(tp)); + std::auto_ptr ctap(new const test_class*(ctp)); + + boost::reference_wrapper tw(t); + boost::reference_wrapper ctw(t); + + id(t).int_member=0; + BOOST_CHECK(id(t).int_member==0); + BOOST_CHECK(cid(t).int_member==0); + BOOST_CHECK(k_m(t)==0); + BOOST_CHECK(ck_m(t)==0); + BOOST_CHECK(cmpk(t)==make_tuple(test_class(0,0),0,0,true)); + BOOST_CHECK(ccmpk(t)==make_tuple(test_class(0,0),0)); + BOOST_CHECK(id(ctr).int_member==0); + BOOST_CHECK(cid(ctr).int_member==0); + BOOST_CHECK(k_m(ctr)==0); + BOOST_CHECK(ck_m(ctr)==0); + BOOST_CHECK(cmpk(ctr)==make_tuple(test_class(0,0),0,0,true)); + BOOST_CHECK(ccmpk(ctr)==make_tuple(test_class(0,0),0)); + + k_m(t)=1; + BOOST_CHECK(id(tp).int_member==1); + BOOST_CHECK(cid(tp).int_member==1); + BOOST_CHECK(k_m(tp)==1); + BOOST_CHECK(ck_m(tp)==1); + BOOST_CHECK(cmpk(tp)==make_tuple(test_class(1,0),1,0,true)); + BOOST_CHECK(ccmpk(tp)==make_tuple(test_class(1,0),1)); + BOOST_CHECK(cid(ctp).int_member==1); + BOOST_CHECK(ck_m(ctp)==1); + BOOST_CHECK(cmpk(ctp)==make_tuple(test_class(1,0),1,0,true)); + BOOST_CHECK(ccmpk(ctp)==make_tuple(test_class(1,0),1)); + + k_m(tp)=2; + BOOST_CHECK(id(tpp).int_member==2); + BOOST_CHECK(cid(tpp).int_member==2); + BOOST_CHECK(k_m(tpp)==2); + BOOST_CHECK(ck_m(tpp)==2); + BOOST_CHECK(cmpk(tpp)==make_tuple(test_class(2,0),2,0,true)); + BOOST_CHECK(ccmpk(tpp)==make_tuple(test_class(2,0),2)); + BOOST_CHECK(cid(ctpp).int_member==2); + BOOST_CHECK(ck_m(ctpp)==2); + BOOST_CHECK(cmpk(ctpp)==make_tuple(test_class(2,0),2,0,true)); + BOOST_CHECK(ccmpk(ctpp)==make_tuple(test_class(2,0),2)); + + k_m(tpp)=3; + BOOST_CHECK(id(tap).int_member==3); + BOOST_CHECK(cid(tap).int_member==3); + BOOST_CHECK(k_m(tap)==3); + BOOST_CHECK(ck_m(tap)==3); + BOOST_CHECK(cmpk(tap)==make_tuple(test_class(3,0),3,0,true)); + BOOST_CHECK(ccmpk(tap)==make_tuple(test_class(3,0),3)); + BOOST_CHECK(cid(ctap).int_member==3); + BOOST_CHECK(ck_m(ctap)==3); + BOOST_CHECK(cmpk(ctap)==make_tuple(test_class(3,0),3,0,true)); + BOOST_CHECK(ccmpk(ctap)==make_tuple(test_class(3,0),3)); + + k_m(tap)=4; + BOOST_CHECK(id(tw).int_member==4); + BOOST_CHECK(cid(tw).int_member==4); + BOOST_CHECK(k_m(tw)==4); + BOOST_CHECK(ck_m(tw)==4); + BOOST_CHECK(cmpk(tw)==make_tuple(test_class(4,0),4,0,true)); + BOOST_CHECK(ccmpk(tw)==make_tuple(test_class(4,0),4)); + + k_m(tw)=5; + BOOST_CHECK(id(ctw).int_member==5); + BOOST_CHECK(cid(ctw).int_member==5); + BOOST_CHECK(k_m(ctw)==5); + BOOST_CHECK(ck_m(ctw)==5); + BOOST_CHECK(cmpk(ctw)==make_tuple(test_class(5,0),5,0,true)); + BOOST_CHECK(ccmpk(ctw)==make_tuple(test_class(5,0),5)); + + BOOST_CHECK(k_cm(t)==0); + + BOOST_CHECK(k_cm(tp)==0); + BOOST_CHECK(k_cm(ctp)==0); + BOOST_CHECK(k_cm(tpp)==0); + BOOST_CHECK(k_cm(ctpp)==0); + BOOST_CHECK(k_cm(tap)==0); + BOOST_CHECK(k_cm(ctap)==0); + + BOOST_CHECK(k_cm(tw)==0); + BOOST_CHECK(k_cm(ctw)==0); + + BOOST_CHECK(k_cmf(t)); + + BOOST_CHECK(k_cmf(tp)); + BOOST_CHECK(k_cmf(ctp)); + BOOST_CHECK(k_cmf(tpp)); + BOOST_CHECK(k_cmf(ctpp)); + BOOST_CHECK(k_cmf(tap)); + BOOST_CHECK(k_cmf(ctap)); + + BOOST_CHECK(k_cmf(tw)); + BOOST_CHECK(k_cmf(ctw)); + + BOOST_CHECK(!k_mf(t)); + + BOOST_CHECK(!k_mf(tp)); + BOOST_CHECK(!k_mf(tpp)); + BOOST_CHECK(!k_mf(tap)); + BOOST_CHECK(!k_mf(tw)); + BOOST_CHECK(ccmpk_w(tw)==make_tuple(false)); + + std::list tl; + for(int i=0;i<20;++i)tl.push_back(test_class(i)); + + int j=0; + for(std::list::iterator it=tl.begin();it!=tl.end();++it){ + BOOST_CHECK(k_m(it)==j); + BOOST_CHECK(k_cm(it)==j); + BOOST_CHECK(k_cmf(it)); + BOOST_CHECK(!k_mf(it)); + BOOST_CHECK(cmpk(it)==make_tuple(test_class(j),j,j,true)); + BOOST_CHECK(ccmpk(it)==make_tuple(test_class(j),j)); + ++j; + } +} diff --git a/test/test_key_extractors.hpp b/test/test_key_extractors.hpp new file mode 100644 index 0000000..ef573ac --- /dev/null +++ b/test/test_key_extractors.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for key extractors. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_key_extractors(); diff --git a/test/test_key_extractors_main.cpp b/test/test_key_extractors_main.cpp new file mode 100644 index 0000000..b191481 --- /dev/null +++ b/test/test_key_extractors_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for key extractors. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_key_extractors.hpp" + +int test_main(int,char *[]) +{ + test_key_extractors(); + return 0; +} diff --git a/test/test_list_ops.cpp b/test/test_list_ops.cpp new file mode 100644 index 0000000..3e00786 --- /dev/null +++ b/test/test_list_ops.cpp @@ -0,0 +1,184 @@ +/* Boost.MultiIndex test for standard list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_list_ops.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include "pre_multi_index.hpp" +#include +#include +#include +#include +#include + +using namespace boost::multi_index; + +#undef _ +#define _ , + +#undef CHECK_EQUAL +#define CHECK_EQUAL(p,check_range) \ +{\ + int v[]=check_range;\ + std::size_t size_v=sizeof(v)/sizeof(int);\ + BOOST_CHECK(std::size_t(std::distance((p).begin(),(p).end()))==size_v);\ + BOOST_CHECK(std::equal((p).begin(),(p).end(),v));\ +} + +#undef CHECK_VOID_RANGE +#define CHECK_VOID_RANGE(p) BOOST_CHECK((p).first==(p).second) + +struct is_even +{ + bool operator()(int x)const{return x%2==0;} +}; + +template +struct same_integral_div +{ + bool operator()(int x,int y)const{return (x/m)==(y/m);} +}; + +template +bool is_sorted( + const Container& c,const Compare& comp=Compare()) +{ + if(c.empty())return true; + + typedef typename Container::const_iterator const_iterator; + for(const_iterator it(c.begin());;){ + const_iterator it2=it; + ++it2; + if(it2==c.end())return true; + if(comp(*it2,*it))return false; + it=it2; + } +} + +void test_list_ops() +{ + typedef multi_index_container< + int, + indexed_by< + ordered_unique >, + sequenced<> + > + > sequenced_set; + typedef nth_index::type sequenced_index; + + sequenced_set ss,ss2; + sequenced_index &si=get<1>(ss),&si2=get<1>(ss2); + + si.push_front(0); /* 0 */ + si.push_front(4); /* 40 */ + ss.insert(2); /* 402 */ + ss.insert(5); /* 4025 */ + si.push_front(3); /* 34025 */ + si.push_back(6); /* 340256 */ + si.push_back(1); /* 3402561 */ + si.insert(project<1>(ss,ss.find(2)),8); /* 34082561 */ + si2=si; + + CHECK_EQUAL(si,{3 _ 4 _ 0 _ 8 _ 2 _ 5 _ 6 _ 1}); + + si.remove(8); + CHECK_EQUAL(si,{3 _ 4 _ 0 _ 2 _ 5 _ 6 _ 1}); + + si.remove_if(is_even()); + + CHECK_EQUAL(si,{3 _ 5 _ 1}); + + si.splice(si.end(),si2); + CHECK_EQUAL(si,{3 _ 5 _ 1 _ 4 _ 0 _ 8 _ 2 _ 6}); + CHECK_EQUAL(si2,{3 _ 5 _ 1}); + + si.splice(project<1>(ss,ss.find(4)),si,project<1>(ss,ss.find(8))); + CHECK_EQUAL(si,{3 _ 5 _ 1 _ 8 _ 4 _ 0 _ 2 _ 6}); + si2.clear(); + si2.splice(si2.begin(),si,si.begin()); + + si.splice(si.end(),si2,si2.begin()); + CHECK_EQUAL(si,{5 _ 1 _ 8 _ 4 _ 0 _ 2 _ 6 _ 3}); + BOOST_CHECK(si2.empty()); + + si2.splice(si2.end(),si,project<1>(ss,ss.find(0)),project<1>(ss,ss.find(6))); + CHECK_EQUAL(si,{5 _ 1 _ 8 _ 4 _ 6 _ 3}); + CHECK_EQUAL(si2,{0 _ 2}); + + si.splice(si.begin(),si,si.begin(),si.begin()); + CHECK_EQUAL(si,{5 _ 1 _ 8 _ 4 _ 6 _ 3}); + + si.splice(project<1>(ss,ss.find(8)),si,project<1>(ss,ss.find(4)),si.end()); + CHECK_EQUAL(si,{5 _ 1 _ 4 _ 6 _ 3 _ 8}); + + si.sort(); + si2.sort(); + BOOST_CHECK(is_sorted(si,std::less())); + BOOST_CHECK(is_sorted(si2,std::less())); + + si.merge(si2); + BOOST_CHECK(is_sorted(si,std::less())); + BOOST_CHECK(si2.empty()); + + { + sequenced_set ss3(ss); + sequenced_index &si3=get<1>(ss3); + + si3.sort(std::greater()); + si.reverse(); + BOOST_CHECK(si==si3); + } + + si2.splice(si2.end(),si,project<1>(ss,ss.find(6)),project<1>(ss,ss.find(3))); + CHECK_EQUAL(si2,{6 _ 5 _ 4}); + + si.merge(si2,std::greater()); + BOOST_CHECK(is_sorted(si,std::greater())); + BOOST_CHECK(si2.empty()); + + typedef multi_index_container< + int, + indexed_by > + > int_list; + + int_list il; + for(int i=0;i<10;++i){ + il.push_back(i); + il.push_back(i); + il.push_front(i); + il.push_front(i); + } /* 9988776655443322110000112233445566778899 */ + + il.unique(); + CHECK_EQUAL( + il, + {9 _ 8 _ 7 _ 6 _ 5 _ 4 _ 3 _ 2 _ 1 _ 0 _ + 1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 _ 9}); + + int_list::iterator it=il.begin(); + for(int j=0;j<9;++j,++it){} /* it points to o */ + + int_list il2; + il2.splice(il2.end(),il,il.begin(),it); + il2.reverse(); + il.merge(il2); + CHECK_EQUAL( + il, + {0 _ 1 _ 1 _ 2 _ 2 _ 3 _ 3 _ 4 _ 4 _ 5 _ 5 _ + 6 _ 6 _ 7 _ 7 _ 8 _ 8 _ 9 _ 9}); + + il.unique(same_integral_div<3>()); + CHECK_EQUAL(il,{0 _ 3 _ 6 _ 9}); + + il.unique(same_integral_div<1>()); + CHECK_EQUAL(il,{0 _ 3 _ 6 _ 9}); +} diff --git a/test/test_list_ops.hpp b/test/test_list_ops.hpp new file mode 100644 index 0000000..6c89f30 --- /dev/null +++ b/test/test_list_ops.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for standard list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_list_ops(); diff --git a/test/test_list_ops_main.cpp b/test/test_list_ops_main.cpp new file mode 100644 index 0000000..006ff72 --- /dev/null +++ b/test/test_list_ops_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for standard list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_list_ops.hpp" + +int test_main(int,char *[]) +{ + test_list_ops(); + return 0; +} diff --git a/test/test_modifiers.cpp b/test/test_modifiers.cpp new file mode 100644 index 0000000..7f7eb3f --- /dev/null +++ b/test/test_modifiers.cpp @@ -0,0 +1,131 @@ +/* Boost.MultiIndex test for modifier memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_modifiers.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_modifiers() +{ + employee_set es; + employee_set_by_age& i2=get(es); + employee_set_as_inserted& i3=get(es); + + es.insert(employee(0,"Joe",31)); + BOOST_CHECK(es.insert(employee(0,"Joe",31)).second==false); + BOOST_CHECK(i2.insert(employee(0,"Joe Jr.",5)).second==false); + BOOST_CHECK(i3.insert(i3.begin(),employee(0,"Joe Jr.",5)).second==false); + BOOST_CHECK(i3.push_front(employee(0,"Joe Jr.",5)).second==false); + BOOST_CHECK(i3.push_back(employee(0,"Joe Jr.",5)).second==false); + + employee_set_by_age::iterator it=i2.find(31); + i2.insert(it,employee(1,"Joe Jr.",5)); + BOOST_CHECK(es.size()==2); + + employee_set_as_inserted::iterator it2=i3.begin(); + i3.insert(it2,100,employee(2,"Grandda Joe",64)); + BOOST_CHECK(es.size()==3); + + es.erase(employee(1,"Joe Jr.",5)); + BOOST_CHECK(i2.size()==2&&i3.size()==2); + + i2.erase(it); + BOOST_CHECK(es.size()==1&&i3.size()==1); + + i3.pop_front(); + BOOST_CHECK(es.size()==0&&i2.size()==0); + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Jack",31)); + i2.erase(31); + BOOST_CHECK(i2.size()==0); + + i3.push_front(employee(1,"Jack",31)); + i3.push_back(employee(0,"Joe",31)); + BOOST_CHECK(i3.front()==employee(1,"Jack",31)); + BOOST_CHECK(i3.back()==employee(0,"Joe",31)); + + i3.pop_back(); + BOOST_CHECK(i3.back()==employee(1,"Jack",31)); + BOOST_CHECK(es.size()==1); + + i3.pop_front(); + BOOST_CHECK(es.size()==0); + + std::vector ve; + ve.push_back(employee(3,"Anna",31)); + ve.push_back(employee(1,"Rachel",27)); + ve.push_back(employee(2,"Agatha",40)); + + i2.insert(ve.begin(),ve.end()); + BOOST_CHECK(i3.size()==3); + + i3.erase(i3.begin(),i3.end()); + BOOST_CHECK(es.size()==0); + + i3.insert(i3.end(),ve.begin(),ve.end()); + BOOST_CHECK(es.size()==3); + + es.erase(es.begin(),es.end()); + BOOST_CHECK(i2.size()==0); + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + employee_set es_backup(es); + + employee_set es2; + es2.insert(employee(3,"Anna",31)); + es2.insert(employee(1,"Rachel",27)); + es2.insert(employee(2,"Agatha",40)); + + employee_set es2_backup(es2); + + i2.swap(get<2>(es2)); + BOOST_CHECK(es==es2_backup&&es2==es_backup); + + i3.swap(get<3>(es2)); + BOOST_CHECK(es==es_backup&&es2==es2_backup); + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)||\ + defined(BOOST_INTEL_CXX_VERSION) + ::boost::multi_index::detail::swap(i2,get<2>(es2)); +#else + using std::swap; + swap(i2,get<2>(es2)); +#endif + + BOOST_CHECK(es==es2_backup&&es2==es_backup); + +#if defined(BOOST_NO_ARGUMENT_DEPENDENT_LOOKUP)||\ + defined(BOOST_INTEL_CXX_VERSION) + ::boost::multi_index::detail::swap(i3,get<3>(es2)); +#else + using std::swap; + swap(i3,get<3>(es2)); +#endif + + BOOST_CHECK(es==es_backup&&es2==es2_backup); + + i3.clear(); + BOOST_CHECK(i3.size()==0); + + es2.clear(); + BOOST_CHECK(es2.size()==0); +} diff --git a/test/test_modifiers.hpp b/test/test_modifiers.hpp new file mode 100644 index 0000000..eca90b6 --- /dev/null +++ b/test/test_modifiers.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for modifier memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_modifiers(); diff --git a/test/test_modifiers_main.cpp b/test/test_modifiers_main.cpp new file mode 100644 index 0000000..70aebfd --- /dev/null +++ b/test/test_modifiers_main.cpp @@ -0,0 +1,20 @@ +/* Boost.MultiIndex test for modifier memfuns. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_modifiers.hpp" + +int test_main(int,char *[]) +{ + test_modifiers(); + return 0; +} + + diff --git a/test/test_mpl_ops.cpp b/test/test_mpl_ops.cpp new file mode 100644 index 0000000..c90544c --- /dev/null +++ b/test/test_mpl_ops.cpp @@ -0,0 +1,75 @@ +/* Boost.MultiIndex test for MPL operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_mpl_ops.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include +#include +#include +#include +#include +#include + +using namespace boost::multi_index; + +void test_mpl_ops() +{ + typedef multi_index_container< + int, + indexed_by< + ordered_unique >, + ordered_non_unique > + > + > indexed_t1; + + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + ordered_unique > >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + ordered_non_unique > >::value)); + + typedef boost::mpl::push_front< + indexed_t1::index_specifier_type_list, + sequenced<> + >::type index_list_t; + + typedef multi_index_container< + int, + index_list_t + > indexed_t2; + + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + sequenced<> >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + boost::mpl::at_c::type>::value)); + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + boost::mpl::at_c::type>::value)); + + typedef multi_index_container< + int, + boost::mpl::list< + ordered_unique >, + ordered_non_unique > + > + > indexed_t3; + + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + boost::mpl::at_c::type>::value)); + BOOST_STATIC_ASSERT((boost::is_same< + boost::mpl::at_c::type, + boost::mpl::at_c::type>::value)); +} diff --git a/test/test_mpl_ops.hpp b/test/test_mpl_ops.hpp new file mode 100644 index 0000000..625de75 --- /dev/null +++ b/test/test_mpl_ops.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for for MPL operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_mpl_ops(); diff --git a/test/test_mpl_ops_main.cpp b/test/test_mpl_ops_main.cpp new file mode 100644 index 0000000..1b24631 --- /dev/null +++ b/test/test_mpl_ops_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for for MPL operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_mpl_ops.hpp" + +int test_main(int,char *[]) +{ + test_mpl_ops(); + return 0; +} diff --git a/test/test_projection.cpp b/test/test_projection.cpp new file mode 100644 index 0000000..fdace29 --- /dev/null +++ b/test/test_projection.cpp @@ -0,0 +1,109 @@ +/* Boost.MultiIndex test for projection capabilities. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_projection.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_projection() +{ + employee_set es; + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + employee_set::iterator it,itbis; + employee_set_by_name::iterator it1; + employee_set_by_age::iterator it2; + employee_set_as_inserted::iterator it3; + + BOOST_STATIC_ASSERT((boost::is_same< + employee_set::iterator, + nth_index_iterator::type >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_name::iterator, + nth_index_iterator::type >::value)); +#if defined(BOOST_NO_MEMBER_TEMPLATES) + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_age::iterator, + index_iterator::type >::value)); +#else + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_age::iterator, + employee_set::index_iterator::type >::value)); +#endif + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_as_inserted::iterator, + nth_index_iterator::type >::value)); + + it= es.find(employee(1,"Robert",27)); + it1= project(es,it); + it2= project(es,it1); + it3= project(es,it2); +#if defined(BOOST_NO_MEMBER_TEMPLATES) + itbis=project<0>(es,it3); +#else + itbis=es.project<0>(it3); +#endif + + BOOST_CHECK(*it==*it1&&*it1==*it2&&*it2==*it3&&itbis==it); + + const employee_set& ces=es; + + employee_set::const_iterator cit,citbis; + employee_set_by_name::const_iterator cit1; + employee_set_by_age::const_iterator cit2; + employee_set_as_inserted::const_iterator cit3; + + BOOST_STATIC_ASSERT((boost::is_same< + employee_set::const_iterator, + nth_index_const_iterator::type >::value)); + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_name::const_iterator, + nth_index_const_iterator::type >::value)); +#if defined(BOOST_NO_MEMBER_TEMPLATES) + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_age::const_iterator, + index_const_iterator::type >::value)); +#else + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_by_age::const_iterator, + employee_set::index_const_iterator::type >::value)); +#endif + BOOST_STATIC_ASSERT((boost::is_same< + employee_set_as_inserted::const_iterator, + nth_index_const_iterator::type >::value)); + + BOOST_CHECK(project(es,es.end())==get(es).end()); + BOOST_CHECK(project(es,es.end())==get(es).end()); + + cit= ces.find(employee(4,"John",57)); +#if defined(BOOST_NO_MEMBER_TEMPLATES) + cit1= project(ces,cit); +#else + cit1= ces.project(cit); +#endif + cit2= project(ces,cit1); +#if defined(BOOST_NO_MEMBER_TEMPLATES) + cit3= project(ces,cit2); +#else + cit3= ces.project(cit2); +#endif + citbis=project<0>(ces,cit3); + + BOOST_CHECK(*cit==*cit1&&*cit1==*cit2&&*cit2==*cit3&&citbis==cit); +} diff --git a/test/test_projection.hpp b/test/test_projection.hpp new file mode 100644 index 0000000..fb381af --- /dev/null +++ b/test/test_projection.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for projection capabilities. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_projection(); diff --git a/test/test_projection_main.cpp b/test/test_projection_main.cpp new file mode 100644 index 0000000..be06652 --- /dev/null +++ b/test/test_projection_main.cpp @@ -0,0 +1,20 @@ +/* Boost.MultiIndex test for projection capabilities. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_projection.hpp" + +int test_main(int,char *[]) +{ + test_projection(); + return 0; +} + + diff --git a/test/test_range.cpp b/test/test_range.cpp new file mode 100644 index 0000000..74d8e27 --- /dev/null +++ b/test/test_range.cpp @@ -0,0 +1,118 @@ +/* Boost.MultiIndex test for range(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_range.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include "pre_multi_index.hpp" +#include +#include +#include +#include + +using namespace boost::multi_index; + +typedef multi_index_container int_set; +typedef int_set::iterator int_set_iterator; + +#undef _ +#define _ , + +#undef CHECK_RANGE +#define CHECK_RANGE(p,check_range) \ +{\ + int v[]=check_range;\ + std::size_t size_v=sizeof(v)/sizeof(int);\ + BOOST_CHECK(std::size_t(std::distance((p).first,(p).second))==size_v);\ + BOOST_CHECK(std::equal((p).first,(p).second,v));\ +} + +#undef CHECK_VOID_RANGE +#define CHECK_VOID_RANGE(p) BOOST_CHECK((p).first==(p).second) + +void test_range() +{ + int_set is; + + for(int i=1;i<=10;++i)is.insert(i); + + std::pair p; + + p=is.range(unbounded,unbounded); + CHECK_RANGE(p,{1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 _ 9 _ 10}); + + p=is.range( + std::bind1st(std::less(),5), /* 5 < x */ + unbounded); + CHECK_RANGE(p,{6 _ 7 _ 8 _ 9 _ 10}); + + p=is.range( + std::bind1st(std::less_equal(),8), /* 8 <= x */ + unbounded); + CHECK_RANGE(p,{8 _ 9 _ 10}); + + p=is.range( + std::bind1st(std::less_equal(),11), /* 11 <= x */ + unbounded); + CHECK_VOID_RANGE(p); + + p=is.range( + unbounded, + std::bind2nd(std::less(),8)); /* x < 8 */ + CHECK_RANGE(p,{1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7}); + + p=is.range( + unbounded, + std::bind2nd(std::less_equal(),4)); /* x <= 4 */ + CHECK_RANGE(p,{1 _ 2 _ 3 _ 4}); + + p=is.range( + unbounded, + std::bind2nd(std::less_equal(),0)); /* x <= 0 */ + CHECK_VOID_RANGE(p); + + p=is.range( + std::bind1st(std::less(),6), /* 6 < x */ + std::bind2nd(std::less_equal(),9)); /* x <= 9 */ + CHECK_RANGE(p,{7 _ 8 _ 9}); + + p=is.range( + std::bind1st(std::less_equal(),4), /* 4 <= x */ + std::bind2nd(std::less(),5)); /* x < 5 */ + CHECK_RANGE(p,{4}); + + p=is.range( + std::bind1st(std::less_equal(),10), /* 10 <= x */ + std::bind2nd(std::less_equal(),10)); /* x <= 10 */ + CHECK_RANGE(p,{10}); + + p=is.range( + std::bind1st(std::less(),0), /* 0 < x */ + std::bind2nd(std::less(),11)); /* x < 11 */ + CHECK_RANGE(p,{1 _ 2 _ 3 _ 4 _ 5 _ 6 _ 7 _ 8 _ 9 _ 10}); + + p=is.range( + std::bind1st(std::less(),7), /* 7 < x */ + std::bind2nd(std::less_equal(),7)); /* x <= 7 */ + CHECK_VOID_RANGE(p); + + p=is.range( + std::bind1st(std::less_equal(),8), /* 8 <= x */ + std::bind2nd(std::less(),2)); /* x < 2 */ + CHECK_VOID_RANGE(p); + + p=is.range( + std::bind1st(std::less(),4), /* 4 < x */ + std::bind2nd(std::less(),5)); /* x < 5 */ + CHECK_VOID_RANGE(p); + BOOST_CHECK(p.first!=is.end()&&p.second!=is.end()); +} diff --git a/test/test_range.hpp b/test/test_range.hpp new file mode 100644 index 0000000..396ac79 --- /dev/null +++ b/test/test_range.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for range(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_range(); diff --git a/test/test_range_main.cpp b/test/test_range_main.cpp new file mode 100644 index 0000000..530abd1 --- /dev/null +++ b/test/test_range_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for range(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_range.hpp" + +int test_main(int,char *[]) +{ + test_range(); + return 0; +} diff --git a/test/test_safe_mode.cpp b/test/test_safe_mode.cpp new file mode 100644 index 0000000..8d9f783 --- /dev/null +++ b/test/test_safe_mode.cpp @@ -0,0 +1,195 @@ +/* Boost.MultiIndex test for safe_mode. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_safe_mode.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include "pair_of_ints.hpp" +#include +#include + +using namespace boost::multi_index; + +#define TRY_SAFE_MODE \ +try{ + +#define CATCH_SAFE_MODE(err) \ + throw std::runtime_error("safe mode violation not detected");\ +}catch(const safe_mode_exception& e){\ + if(e.error_code!=(err))throw std::runtime_error(\ + "safe mode violation not expected");\ +} + +struct change_id +{ + change_id(int new_id_):new_id(new_id_){} + void operator()(employee& e){e.id=new_id;} + +private: + int new_id; +}; + +typedef multi_index_container< + pair_of_ints, + indexed_by< + ordered_unique, + ordered_unique > > +int_int_set; + +void test_safe_mode() +{ + employee_set es,es2; + employee_set_as_inserted& i=get(es); + es.insert(employee(0,"Joe",31)); + + TRY_SAFE_MODE + employee_set::iterator it; + employee e=*it; + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.end(); + employee e=*it; + CATCH_SAFE_MODE(safe_mode::not_dereferenceable_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.end(); + ++it; + CATCH_SAFE_MODE(safe_mode::not_incrementable_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.begin(); + --it; + CATCH_SAFE_MODE(safe_mode::not_decrementable_iterator) + + TRY_SAFE_MODE + employee_set::iterator it; + employee_set::iterator it2; + bool b=(it==it2); + b=true; /* avoid warning about unused var */ + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.begin(); + employee_set::iterator it2; + bool b=(it==it2); + b=true; /* avoid warning about unused var */ + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.begin(); + employee_set::iterator it2=es2.begin(); + bool b=(it==it2); + b=true; /* avoid warning about unused var */ + CATCH_SAFE_MODE(safe_mode::not_same_owner) + + TRY_SAFE_MODE + es.erase(es.end(),es.begin()); + CATCH_SAFE_MODE(safe_mode::invalid_range) + + TRY_SAFE_MODE + employee_set::iterator it; + es.insert(it,employee(0,"Joe",31)); + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + es.erase(es.end()); + CATCH_SAFE_MODE(safe_mode::not_dereferenceable_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.begin(); + es2.insert(it,employee(0,"Joe",31)); + CATCH_SAFE_MODE(safe_mode::not_owner) + + TRY_SAFE_MODE + employee_set::iterator it=es.begin(); + employee_set::iterator it2=es2.end(); + es2.erase(it,it2); + CATCH_SAFE_MODE(safe_mode::not_owner) + + TRY_SAFE_MODE + employee_set::iterator it=es.insert(employee(1,"Robert",27)).first; + es.erase(it); + es.erase(it); + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set::iterator it; + { + employee_set es3; + it=es3.insert(employee(0,"Joe",31)).first; + } + employee e=*it; + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set es3(es); + employee_set es4; + employee_set::iterator it=es3.begin(); + es3.swap(es4); + es3.erase(it); + CATCH_SAFE_MODE(safe_mode::not_owner) + + /* this, unlike the previous case, is indeed correct, test safe mode + * gets it right + */ + { + employee_set es3(es); + employee_set es4; + employee_set::iterator it=es3.begin(); + es3.swap(es4); + es4.erase(it); + } + + TRY_SAFE_MODE + employee_set::iterator it=es.insert(employee(1,"Robert",27)).first; + employee_set_by_name::iterator it2=project(es,it); + es.modify_key(it,change_id(0)); + employee e=*it2; + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + int_int_set iis; + int_int_set::iterator it=iis.insert(pair_of_ints(0,0)).first; + iis.insert(pair_of_ints(1,1)); + iis.modify(it,increment_first); + pair_of_ints p=*it; + p.first=0; /* avoid warning about unused var */ + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + int_int_set iis; + int_int_set::iterator it=iis.insert(pair_of_ints(0,0)).first; + iis.insert(pair_of_ints(1,1)); + iis.modify(it,increment_second); + pair_of_ints p=*it; + p.first=0; /* avoid warning about unused var */ + CATCH_SAFE_MODE(safe_mode::invalid_iterator) + + TRY_SAFE_MODE + employee_set::iterator it=es.end(); + employee_set_by_name::iterator it2=project(es2,it); + CATCH_SAFE_MODE(safe_mode::not_owner) + + TRY_SAFE_MODE + employee_set_by_name::iterator it=get(es).end(); + employee_set::iterator it2=project<0>(es2,it); + CATCH_SAFE_MODE(safe_mode::not_owner) + + TRY_SAFE_MODE + i.splice(i.begin(),i,i.begin(),i.end()); + CATCH_SAFE_MODE(safe_mode::inside_range) + + TRY_SAFE_MODE + i.splice(i.begin(),i); + CATCH_SAFE_MODE(safe_mode::same_container) +} diff --git a/test/test_safe_mode.hpp b/test/test_safe_mode.hpp new file mode 100644 index 0000000..4597b1e --- /dev/null +++ b/test/test_safe_mode.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for safe mode. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_safe_mode(); diff --git a/test/test_safe_mode_main.cpp b/test/test_safe_mode_main.cpp new file mode 100644 index 0000000..382f2f8 --- /dev/null +++ b/test/test_safe_mode_main.cpp @@ -0,0 +1,19 @@ +/* Boost.MultiIndex test for safe mode. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_safe_mode.hpp" + +int test_main(int,char *[]) +{ + test_safe_mode(); + return 0; +} + diff --git a/test/test_set_ops.cpp b/test/test_set_ops.cpp new file mode 100644 index 0000000..38a7d1d --- /dev/null +++ b/test/test_set_ops.cpp @@ -0,0 +1,50 @@ +/* Boost.MultiIndex test for standard set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_set_ops.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +void test_set_ops() +{ + employee_set es; + employee_set_by_name& i1=get(es); + const employee_set_by_age& i2=get(es); + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Albert",20)); + es.insert(employee(4,"John",57)); + + BOOST_CHECK(i1.find("John")->name=="John"); + BOOST_CHECK(i2.find(41)==i2.end()); + + BOOST_CHECK(i1.count("John")==2); + BOOST_CHECK(es.count(employee(10,"",-1))==0); + + BOOST_CHECK(i1.lower_bound("John")->name=="John"); + + BOOST_CHECK( + std::distance( + i2.lower_bound(31), + i2.upper_bound(60))==3); + + std::pair p= + i1.equal_range("John"); + BOOST_CHECK(std::distance(p.first,p.second)==2); +} diff --git a/test/test_set_ops.hpp b/test/test_set_ops.hpp new file mode 100644 index 0000000..98128a6 --- /dev/null +++ b/test/test_set_ops.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for standard set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_set_ops(); diff --git a/test/test_set_ops_main.cpp b/test/test_set_ops_main.cpp new file mode 100644 index 0000000..f233c24 --- /dev/null +++ b/test/test_set_ops_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for standard set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_set_ops.hpp" + +int test_main(int,char *[]) +{ + test_set_ops(); + return 0; +} diff --git a/test/test_special_list_ops.cpp b/test/test_special_list_ops.cpp new file mode 100644 index 0000000..61a9a06 --- /dev/null +++ b/test/test_special_list_ops.cpp @@ -0,0 +1,83 @@ +/* Boost.MultiIndex test for special list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_special_list_ops.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include +#include "pre_multi_index.hpp" +#include +#include +#include + +using namespace boost::multi_index; + +#undef _ +#define _ , + +#undef CHECK_EQUAL +#define CHECK_EQUAL(p,check_range) \ +{\ + int v[]=check_range;\ + std::size_t size_v=sizeof(v)/sizeof(int);\ + BOOST_CHECK(std::size_t(std::distance((p).begin(),(p).end()))==size_v);\ + BOOST_CHECK(std::equal((p).begin(),(p).end(),v));\ +} + +#undef CHECK_VOID_RANGE +#define CHECK_VOID_RANGE(p) BOOST_CHECK((p).first==(p).second) + +void test_special_list_ops() +{ + typedef multi_index_container< + int, + indexed_by< + sequenced<> + > + > sequenced_container; + + sequenced_container sc; + sc.push_back(0); + sc.push_back(1); + sc.push_back(2); + sc.push_back(3); + sc.push_back(4); + sc.push_back(5); + + sequenced_container::iterator it; + + it=sc.begin(); + std::advance(it,3); + sc.relocate(sc.begin(),it); + CHECK_EQUAL(sc,{3 _ 0 _ 1 _ 2 _ 4 _ 5}); + BOOST_CHECK(it==sc.begin()); + + sc.relocate(it,it); + CHECK_EQUAL(sc,{3 _ 0 _ 1 _ 2 _ 4 _ 5}); + + std::advance(it,3); + sc.relocate(sc.end(),it,sc.end()); + CHECK_EQUAL(sc,{3 _ 0 _ 1 _ 2 _ 4 _ 5}); + + sc.relocate(sc.begin(),it,it); + CHECK_EQUAL(sc,{3 _ 0 _ 1 _ 2 _ 4 _ 5}); + + sequenced_container::iterator it2; + + it2=sc.begin(); + ++it2; + sc.relocate(it2,it,sc.end()); + CHECK_EQUAL(sc,{3 _ 2 _ 4 _ 5 _ 0 _ 1}); + BOOST_CHECK(std::distance(it,it2)==3); + + sc.relocate(--(sc.end()),it,it2); + CHECK_EQUAL(sc,{3 _ 0 _ 2 _ 4 _ 5 _ 1}); +} diff --git a/test/test_special_list_ops.hpp b/test/test_special_list_ops.hpp new file mode 100644 index 0000000..fc95f54 --- /dev/null +++ b/test/test_special_list_ops.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for special list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_special_list_ops(); diff --git a/test/test_special_list_ops_main.cpp b/test/test_special_list_ops_main.cpp new file mode 100644 index 0000000..bee82a9 --- /dev/null +++ b/test/test_special_list_ops_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for special list operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_special_list_ops.hpp" + +int test_main(int,char *[]) +{ + test_special_list_ops(); + return 0; +} diff --git a/test/test_special_set_ops.cpp b/test/test_special_set_ops.cpp new file mode 100644 index 0000000..350b73c --- /dev/null +++ b/test/test_special_set_ops.cpp @@ -0,0 +1,59 @@ +/* Boost.MultiIndex test for special set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_special_set_ops.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include + +using namespace boost::multi_index; + +struct comp_initial +{ + bool operator()(char ch,const std::string& s)const + { + if(s.empty())return false; + return ch(es).lower_bound('J',comp_initial()), + get(es).upper_bound('J',comp_initial()))==3); + + BOOST_CHECK(get(es).find('A',comp_initial())->name[0]=='A'); + + BOOST_CHECK( + std::distance( + get(es).lower_bound(27), + get(es).upper_bound(40))==3); + + BOOST_CHECK(es.count(2,employee::comp_id())==1); + BOOST_CHECK(es.count(5,employee::comp_id())==0); +} diff --git a/test/test_special_set_ops.hpp b/test/test_special_set_ops.hpp new file mode 100644 index 0000000..78d4ce2 --- /dev/null +++ b/test/test_special_set_ops.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for special set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_special_set_ops(); diff --git a/test/test_special_set_ops_main.cpp b/test/test_special_set_ops_main.cpp new file mode 100644 index 0000000..4530df6 --- /dev/null +++ b/test/test_special_set_ops_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for special set operations. + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_special_set_ops.hpp" + +int test_main(int,char *[]) +{ + test_special_set_ops(); + return 0; +} diff --git a/test/test_update.cpp b/test/test_update.cpp new file mode 100644 index 0000000..b17b9ef --- /dev/null +++ b/test/test_update.cpp @@ -0,0 +1,86 @@ +/* Boost.MultiIndex test for replace(), modify() and modify_key(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include "test_update.hpp" + +#include /* keep it first to prevent nasty warns in MSVC */ +#include +#include "pre_multi_index.hpp" +#include "employee.hpp" +#include "pair_of_ints.hpp" +#include + +using namespace boost::multi_index; + +void test_update() +{ + employee_set es; + employee_set_as_inserted& i=get(es); + + es.insert(employee(0,"Joe",31)); + es.insert(employee(1,"Robert",27)); + es.insert(employee(2,"John",40)); + es.insert(employee(3,"Olbert",20)); + es.insert(employee(4,"John",57)); + + employee_set::iterator it=es.find(employee(0,"Joe",31)); + employee_set_as_inserted::iterator it1= + project(es,get(es).find("Olbert")); + + BOOST_CHECK(es.replace(it,*it)); + BOOST_CHECK(!es.replace(it,employee(3,"Joe",31))&&it->id==0); + BOOST_CHECK(es.replace(it,employee(0,"Joe",32))&&it->age==32); + BOOST_CHECK(i.replace(it1,employee(3,"Albert",20))&&it1->name=="Albert"); + + typedef multi_index_container< + pair_of_ints, + indexed_by< + ordered_unique, + ordered_unique, + sequenced<> > > + int_int_set; + + int_int_set iis; + nth_index::type& iii=get<2>(iis); + iis.insert(pair_of_ints(0,0)); + iis.insert(pair_of_ints(5,5)); + iis.insert(pair_of_ints(10,10)); + + BOOST_CHECK(!iis.replace(iis.begin(),pair_of_ints(5,0))); + BOOST_CHECK(!iii.replace(iii.begin(),pair_of_ints(0,5))); + BOOST_CHECK(!iis.replace(iis.begin(),pair_of_ints(5,11))); + BOOST_CHECK(!iis.replace(iis.begin(),pair_of_ints(11,5))); + + BOOST_CHECK(iis.modify(iis.begin(),increment_first)); + BOOST_CHECK(iii.modify(iii.begin(),increment_first)); + BOOST_CHECK(iis.modify(iis.begin(),increment_first)); + BOOST_CHECK(iii.modify(iii.begin(),increment_first)); + + BOOST_CHECK(!iis.modify(iis.begin(),increment_first)); + BOOST_CHECK(iis.size()==2); + + iis.insert(pair_of_ints(0,0)); + BOOST_CHECK(iii.modify(--iii.end(),increment_second)); + BOOST_CHECK(iis.modify(iis.begin(),increment_second)); + BOOST_CHECK(iii.modify(--iii.end(),increment_second)); + BOOST_CHECK(iis.modify(iis.begin(),increment_second)); + + BOOST_CHECK(!iii.modify(--iii.end(),increment_second)); + BOOST_CHECK(iii.size()==2); + + iis.insert(pair_of_ints(0,0)); + BOOST_CHECK(iis.modify_key(iis.begin(),increment_int)); + BOOST_CHECK(iis.modify_key(iis.begin(),increment_int)); + BOOST_CHECK(iis.modify_key(iis.begin(),increment_int)); + BOOST_CHECK(iis.modify_key(iis.begin(),increment_int)); + + BOOST_CHECK(!iis.modify_key(iis.begin(),increment_int)); + BOOST_CHECK(iis.size()==2); +} diff --git a/test/test_update.hpp b/test/test_update.hpp new file mode 100644 index 0000000..c6fad09 --- /dev/null +++ b/test/test_update.hpp @@ -0,0 +1,11 @@ +/* Boost.MultiIndex test for replace(), modify() and modify_key(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +void test_update(); diff --git a/test/test_update_main.cpp b/test/test_update_main.cpp new file mode 100644 index 0000000..ebc850a --- /dev/null +++ b/test/test_update_main.cpp @@ -0,0 +1,18 @@ +/* Boost.MultiIndex test for replace(), modify() and modify_key(). + * + * Copyright Joaquín M López Muñoz 2003-2004. Use, modification, and + * distribution are subject to the Boost Software License, Version 1.0. + * (See accompanying file LICENSE_1_0.txt or copy at + * http://www.boost.org/LICENSE_1_0.txt) + * + * See http://www.boost.org/libs/multi_index for library home page. + */ + +#include +#include "test_update.hpp" + +int test_main(int,char *[]) +{ + test_update(); + return 0; +}