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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∨MS*U27l0m> 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

GX6cxvB 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 Qh#ov&`vv*l_F?k>2Zh?V&j0`b literal 0 HcmV?d00001 diff --git a/doc/next.gif b/doc/next.gif new file mode 100644 index 0000000000000000000000000000000000000000..d6c18a578d75a4e0ae47981af259b164603e0864 GIT binary patch literal 852 zcmV-a1FQT;Nk%w1VGsZi0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui01yBW000O%0098_N3ftlg8~H}T=*|xLWTt$I)o@u;=+m*5n{~P eaHGeMATbgQsd3{)kqkLfdRj)ANqiiA{7kE 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 z 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 C6X_}d 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 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 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 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 P0{<;=@o=tiWT*Tu27_Ps literal 0 HcmV?d00001 diff --git a/doc/perf_2o1s.png b/doc/perf_2o1s.png new file mode 100644 index 0000000000000000000000000000000000000000..e25371f2d8469e1b5546ab780d520898e404b5c2 GIT binary patch literal 13406 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 JR?%6>{|lCyMa2LB 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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 P6!^1jsi#{djmh~BDmB<~ 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 zcmV-a1FQT;Nk%w1VGsZi0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui01yBW000O%0RIUbNRS`^00k5NJ9tpxK!yw*LWG!b;zWZMFD}f; ev7^V20zXE42(q9@i5VS+)TnY}! + + + + +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 zcmV-b1FHN-Nk%w1VGsZi0QUd@000010RaL60s{jB1Ox;H1qB8M1_uWR2nYxX2?+`c z3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85tTH8XFrM z92^`S9UUGX9v>ecARr(iAt53nA|oRsBqSsyB_$>%CMPE+C@3f?DJd!{Dl021EG#T7 zEiEoCE-x=HFfcGNF)=bSGBYzXG&D3dH8nOiHa9mnI5;>tIXOByIy*Z%JUl!-Jv}}? zK0iM{KtMo2K|w-7LPJACL_|bIMMXwNMn^|SNJvOYNl8jdN=r*iOiWBoO-)WtPESuy zP*6}&QBhJ-Qd3h?R8&+|RaI72R##V7SXfwDSy@_IT3cINTwGjTU0q&YUSD5dU|?Wj zVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@X=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2T za&vQYbaZreb#-=jc6WDoczAeud3kzzdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyD zgoK2Jg@uNOhKGlTh=_=ZiHVAeii?YjjEszpjg5|uj*pLzkdTm(k&%*;l9Q8@l$4Z} zm6ev3mY0{8n3$NEnVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAguq@<*!rKP5( zrl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIfwY9dkwzs#p zxVX5vxw*Q!y1To(yu7@dCU$jHda z$;ryf%FD~k%*@Qq&CSlv&d<-!(9qD)(b3Y<($mw^)YR0~)z#M4*4Nk9*x1lt)=I7_<=;-L_>FMg~>g((4 z?Ck9A?d|UF?(gsK@bK{Q@$vHV^7Hfa^z`)g_4W4l_V@Sq`1ttw`T6?#`uqF){QUg= z{r&#_{{R2~EC2ui01yBW000O&0RIUb_zwU8f(Q{BOvupSLWTq#9)u`R;zNiQCpOHe fQ6a~T96MGFDUzhah$c0rJXvt%L6$(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; +}