diff --git a/.drone.jsonnet b/.drone.jsonnet index adedd31..dac39c2 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -347,7 +347,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") = linux_pipeline( "Linux 18.04 Clang 5.0", "cppalliance/droneubuntu1804:1", - { TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '11,14,1z' }, + { TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '11,14' }, "clang-5.0", ), diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34be361..90de1da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,7 +90,7 @@ jobs: install: clang-4.0 - toolset: clang compiler: clang++-5.0 - cxxstd: "11,14,1z" + cxxstd: "11,14" os: ubuntu-latest container: ubuntu:18.04 install: clang-5.0 diff --git a/doc/closed_poly_collection_synopsis.qbk b/doc/closed_poly_collection_synopsis.qbk new file mode 100644 index 0000000..b18cba9 --- /dev/null +++ b/doc/closed_poly_collection_synopsis.qbk @@ -0,0 +1,147 @@ +[/ + Copyright 2024 Joaquin M Lopez Munoz. + Distributed under 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) +] + +[template closed_poly_collection_synopsis[class_name template_header value_type type_index] + ``[template_header]`` + class ``[class_name]`` + { + public: + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.types ['// types:]]`` + + using value_type=``[value_type]``; + using allocator_type=Allocator; + using size_type=std::size_t; + using difference_type=std::ptrdiff_t; + using reference=value_type&; + using const_reference=const value_type&; + using pointer=typename std::allocator_traits::pointer; + using const_pointer=typename std::allocator_traits::const_pointer; + using _type_index_=``[type_index]``; + using iterator=``/implementation-defined/``; + using const_iterator=``/implementation-defined/``; + using _local_base_iterator_=``/implementation-defined/``; + using _const_local_base_iterator_=``/implementation-defined/``; + template using _local_iterator_=``/implementation-defined/``; + template using _const_local_iterator_=``/implementation-defined/``; + class _const_base_segment_info_; + class _base_segment_info_; + template class _const_segment_info_; + template class _segment_info_; + using _base_segment_info_iterator_=``/implementation-defined/``; + using _const_base_segment_info_iterator_=``/implementation-defined/``; + class _const_segment_traversal_info_; + class _segment_traversal_info_; + + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.construct_copy_destroy ['// construct/destroy/copy:]]`` + + ``[class_name]``(); + ``[class_name]``(const ``[class_name]``&); + ``[class_name]``(``[class_name]``&&); + explicit ``[class_name]``(const allocator_type& al); + ``[class_name]``(const ``[class_name]``& x,const allocator_type& al); + ``[class_name]``(``[class_name]``&& x,const allocator_type& al); + template + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.construct_copy_destroy.range_construction [class_name]]``( + InputIterator first,InputIterator last, + const allocator_type& al=allocator_type{}); + + ``[class_name]``& operator=(const ``[class_name]``&); + ``[class_name]``& operator=(``[class_name]``&&); + + allocator_type get_allocator()const noexcept; + + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.iterators ['// iterators:]]`` + + iterator _begin_()noexcept; + iterator _end_()noexcept; + const_iterator _begin_()const noexcept; + const_iterator _end_()const noexcept; + const_iterator _cbegin_()const noexcept; + const_iterator _cend_()const noexcept; + + local_base_iterator _begin_(const type_index& info); + local_base_iterator _end_(const type_index& info); + const_local_base_iterator _begin_(const type_index& info)const; + const_local_base_iterator _end_(const type_index& info)const; + const_local_base_iterator _cbegin_(const type_index& info)const; + const_local_base_iterator _cend_(const type_index& info)const; + + template local_iterator _begin_(); + template local_iterator _end_(); + template const_local_iterator _begin_()const; + template const_local_iterator _end_()const; + template const_local_iterator _cbegin_()const; + template const_local_iterator _cend_()const; + + base_segment_info _segment_(const type_index& info); + const_base_segment_info _segment_(const type_index& info)const; + template segment_info _segment_(); + template const_segment_info _segment_()const; + + segment_traversal_info _segment_traversal_()noexcept; + const_segment_traversal_info _segment_traversal_()const noexcept; + + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.capacity ['// capacity:]]`` + + bool empty()const noexcept; + bool _empty_(const type_index& info)const; + template bool _empty_()const; + + size_type size()const noexcept; + size_type _size_(const type_index& info)const; + template size_type _size_()const; + + size_type _max_size_(const type_index& info)const; + template size_type _max_size_()const; + + size_type _capacity_(const type_index& info)const; + template size_type _capacity_()const; + + void _reserve_(size_type n); + void _reserve_(const type_index& info,size_type n); + templatevoid _reserve_(size_type n); + + void _shrink_to_fit_(); + void _shrink_to_fit_(const type_index& info); + template void _shrink_to_fit_(); + + ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.modifiers ['// modifiers:]]`` + + template + iterator _emplace_(Args&&... args); + + template + iterator _emplace_hint_(const_iterator hint,Args&&... args); + + template + auto _emplace_pos_(LocalIterator pos,Args&&... args); + + template + iterator _insert_(T&& x); + + template + auto _insert_hint_(CollectionIterator hint,T&& x); + + template + void _insert_range_(InputIterator first,InputIterator last); + + template + void _insert_hint_range_(CollectionIterator hint,InputIterator first,InputIterator last); + + template + auto _erase_(CollectionIterator pos); + + template + auto _erase_(CollectionIterator first,CollectionIterator last); + + void _clear_()noexcept; + void _clear_(const type_index& info); + template void _clear_(); + + void swap(``[class_name]``& x); + }; +] \ No newline at end of file diff --git a/doc/img/for_each_any_clang130_x64.png b/doc/img/for_each_any_clang130_x64.png new file mode 100644 index 0000000..2f80f99 Binary files /dev/null and b/doc/img/for_each_any_clang130_x64.png differ diff --git a/doc/img/for_each_any_clang40_x64.png b/doc/img/for_each_any_clang40_x64.png deleted file mode 100644 index bfac8c9..0000000 Binary files a/doc/img/for_each_any_clang40_x64.png and /dev/null differ diff --git a/doc/img/for_each_any_gcc132_x64.png b/doc/img/for_each_any_gcc132_x64.png new file mode 100644 index 0000000..cf9e76c Binary files /dev/null and b/doc/img/for_each_any_gcc132_x64.png differ diff --git a/doc/img/for_each_any_gcc63_x64.png b/doc/img/for_each_any_gcc63_x64.png deleted file mode 100644 index 13334de..0000000 Binary files a/doc/img/for_each_any_gcc63_x64.png and /dev/null differ diff --git a/doc/img/for_each_any_vs2015_x64.png b/doc/img/for_each_any_vs2015_x64.png deleted file mode 100644 index 9a5b0da..0000000 Binary files a/doc/img/for_each_any_vs2015_x64.png and /dev/null differ diff --git a/doc/img/for_each_any_vs2015_x86.png b/doc/img/for_each_any_vs2015_x86.png deleted file mode 100644 index 19a1261..0000000 Binary files a/doc/img/for_each_any_vs2015_x86.png and /dev/null differ diff --git a/doc/img/for_each_any_vs2022_x64.png b/doc/img/for_each_any_vs2022_x64.png new file mode 100644 index 0000000..0eec6b9 Binary files /dev/null and b/doc/img/for_each_any_vs2022_x64.png differ diff --git a/doc/img/for_each_base_clang130_x64.png b/doc/img/for_each_base_clang130_x64.png new file mode 100644 index 0000000..bdddbf2 Binary files /dev/null and b/doc/img/for_each_base_clang130_x64.png differ diff --git a/doc/img/for_each_base_clang40_x64.png b/doc/img/for_each_base_clang40_x64.png deleted file mode 100644 index 977f5d2..0000000 Binary files a/doc/img/for_each_base_clang40_x64.png and /dev/null differ diff --git a/doc/img/for_each_base_gcc132_x64.png b/doc/img/for_each_base_gcc132_x64.png new file mode 100644 index 0000000..bfb96b4 Binary files /dev/null and b/doc/img/for_each_base_gcc132_x64.png differ diff --git a/doc/img/for_each_base_gcc63_x64.png b/doc/img/for_each_base_gcc63_x64.png deleted file mode 100644 index 4b85d7c..0000000 Binary files a/doc/img/for_each_base_gcc63_x64.png and /dev/null differ diff --git a/doc/img/for_each_base_vs2015_x64.png b/doc/img/for_each_base_vs2015_x64.png deleted file mode 100644 index 9a22680..0000000 Binary files a/doc/img/for_each_base_vs2015_x64.png and /dev/null differ diff --git a/doc/img/for_each_base_vs2015_x86.png b/doc/img/for_each_base_vs2015_x86.png deleted file mode 100644 index 2ee04d0..0000000 Binary files a/doc/img/for_each_base_vs2015_x86.png and /dev/null differ diff --git a/doc/img/for_each_base_vs2022_x64.png b/doc/img/for_each_base_vs2022_x64.png new file mode 100644 index 0000000..4dc7c13 Binary files /dev/null and b/doc/img/for_each_base_vs2022_x64.png differ diff --git a/doc/img/for_each_function_clang130_x64.png b/doc/img/for_each_function_clang130_x64.png new file mode 100644 index 0000000..e458542 Binary files /dev/null and b/doc/img/for_each_function_clang130_x64.png differ diff --git a/doc/img/for_each_function_clang40_x64.png b/doc/img/for_each_function_clang40_x64.png deleted file mode 100644 index 344acca..0000000 Binary files a/doc/img/for_each_function_clang40_x64.png and /dev/null differ diff --git a/doc/img/for_each_function_gcc132_x64.png b/doc/img/for_each_function_gcc132_x64.png new file mode 100644 index 0000000..843ac3b Binary files /dev/null and b/doc/img/for_each_function_gcc132_x64.png differ diff --git a/doc/img/for_each_function_gcc63_x64.png b/doc/img/for_each_function_gcc63_x64.png deleted file mode 100644 index c79a23a..0000000 Binary files a/doc/img/for_each_function_gcc63_x64.png and /dev/null differ diff --git a/doc/img/for_each_function_vs2015_x64.png b/doc/img/for_each_function_vs2015_x64.png deleted file mode 100644 index 2347b5c..0000000 Binary files a/doc/img/for_each_function_vs2015_x64.png and /dev/null differ diff --git a/doc/img/for_each_function_vs2015_x86.png b/doc/img/for_each_function_vs2015_x86.png deleted file mode 100644 index a6af668..0000000 Binary files a/doc/img/for_each_function_vs2015_x86.png and /dev/null differ diff --git a/doc/img/for_each_function_vs2022_x64.png b/doc/img/for_each_function_vs2022_x64.png new file mode 100644 index 0000000..3559b45 Binary files /dev/null and b/doc/img/for_each_function_vs2022_x64.png differ diff --git a/doc/img/for_each_variant_clang130_x64.png b/doc/img/for_each_variant_clang130_x64.png new file mode 100644 index 0000000..355d428 Binary files /dev/null and b/doc/img/for_each_variant_clang130_x64.png differ diff --git a/doc/img/for_each_variant_gcc132_x64.png b/doc/img/for_each_variant_gcc132_x64.png new file mode 100644 index 0000000..e512296 Binary files /dev/null and b/doc/img/for_each_variant_gcc132_x64.png differ diff --git a/doc/img/for_each_variant_vs2022_x64.png b/doc/img/for_each_variant_vs2022_x64.png new file mode 100644 index 0000000..8228914 Binary files /dev/null and b/doc/img/for_each_variant_vs2022_x64.png differ diff --git a/doc/img/insert_any_clang130_x64.png b/doc/img/insert_any_clang130_x64.png new file mode 100644 index 0000000..f421921 Binary files /dev/null and b/doc/img/insert_any_clang130_x64.png differ diff --git a/doc/img/insert_any_clang40_x64.png b/doc/img/insert_any_clang40_x64.png deleted file mode 100644 index afd52c9..0000000 Binary files a/doc/img/insert_any_clang40_x64.png and /dev/null differ diff --git a/doc/img/insert_any_gcc132_x64.png b/doc/img/insert_any_gcc132_x64.png new file mode 100644 index 0000000..4274e9d Binary files /dev/null and b/doc/img/insert_any_gcc132_x64.png differ diff --git a/doc/img/insert_any_gcc63_x64.png b/doc/img/insert_any_gcc63_x64.png deleted file mode 100644 index 3503f70..0000000 Binary files a/doc/img/insert_any_gcc63_x64.png and /dev/null differ diff --git a/doc/img/insert_any_vs2015_x64.png b/doc/img/insert_any_vs2015_x64.png deleted file mode 100644 index 7d6db2a..0000000 Binary files a/doc/img/insert_any_vs2015_x64.png and /dev/null differ diff --git a/doc/img/insert_any_vs2015_x86.png b/doc/img/insert_any_vs2015_x86.png deleted file mode 100644 index 1c85ed3..0000000 Binary files a/doc/img/insert_any_vs2015_x86.png and /dev/null differ diff --git a/doc/img/insert_any_vs2022_x64.png b/doc/img/insert_any_vs2022_x64.png new file mode 100644 index 0000000..1c85a2e Binary files /dev/null and b/doc/img/insert_any_vs2022_x64.png differ diff --git a/doc/img/insert_base_clang130_x64.png b/doc/img/insert_base_clang130_x64.png new file mode 100644 index 0000000..9fc2854 Binary files /dev/null and b/doc/img/insert_base_clang130_x64.png differ diff --git a/doc/img/insert_base_clang40_x64.png b/doc/img/insert_base_clang40_x64.png deleted file mode 100644 index 440b643..0000000 Binary files a/doc/img/insert_base_clang40_x64.png and /dev/null differ diff --git a/doc/img/insert_base_gcc132_x64.png b/doc/img/insert_base_gcc132_x64.png new file mode 100644 index 0000000..8ee43a7 Binary files /dev/null and b/doc/img/insert_base_gcc132_x64.png differ diff --git a/doc/img/insert_base_gcc63_x64.png b/doc/img/insert_base_gcc63_x64.png deleted file mode 100644 index d130cf0..0000000 Binary files a/doc/img/insert_base_gcc63_x64.png and /dev/null differ diff --git a/doc/img/insert_base_vs2015_x64.png b/doc/img/insert_base_vs2015_x64.png deleted file mode 100644 index b87bf10..0000000 Binary files a/doc/img/insert_base_vs2015_x64.png and /dev/null differ diff --git a/doc/img/insert_base_vs2015_x86.png b/doc/img/insert_base_vs2015_x86.png deleted file mode 100644 index 956dec1..0000000 Binary files a/doc/img/insert_base_vs2015_x86.png and /dev/null differ diff --git a/doc/img/insert_base_vs2022_x64.png b/doc/img/insert_base_vs2022_x64.png new file mode 100644 index 0000000..80569e8 Binary files /dev/null and b/doc/img/insert_base_vs2022_x64.png differ diff --git a/doc/img/insert_function_clang130_x64.png b/doc/img/insert_function_clang130_x64.png new file mode 100644 index 0000000..ed759da Binary files /dev/null and b/doc/img/insert_function_clang130_x64.png differ diff --git a/doc/img/insert_function_clang40_x64.png b/doc/img/insert_function_clang40_x64.png deleted file mode 100644 index e1cf18e..0000000 Binary files a/doc/img/insert_function_clang40_x64.png and /dev/null differ diff --git a/doc/img/insert_function_gcc132_x64.png b/doc/img/insert_function_gcc132_x64.png new file mode 100644 index 0000000..bf64f71 Binary files /dev/null and b/doc/img/insert_function_gcc132_x64.png differ diff --git a/doc/img/insert_function_gcc63_x64.png b/doc/img/insert_function_gcc63_x64.png deleted file mode 100644 index f015ebc..0000000 Binary files a/doc/img/insert_function_gcc63_x64.png and /dev/null differ diff --git a/doc/img/insert_function_vs2015_x64.png b/doc/img/insert_function_vs2015_x64.png deleted file mode 100644 index b09d286..0000000 Binary files a/doc/img/insert_function_vs2015_x64.png and /dev/null differ diff --git a/doc/img/insert_function_vs2015_x86.png b/doc/img/insert_function_vs2015_x86.png deleted file mode 100644 index 74f1190..0000000 Binary files a/doc/img/insert_function_vs2015_x86.png and /dev/null differ diff --git a/doc/img/insert_function_vs2022_x64.png b/doc/img/insert_function_vs2022_x64.png new file mode 100644 index 0000000..33f75ae Binary files /dev/null and b/doc/img/insert_function_vs2022_x64.png differ diff --git a/doc/img/insert_variant_clang130_x64.png b/doc/img/insert_variant_clang130_x64.png new file mode 100644 index 0000000..7ea2079 Binary files /dev/null and b/doc/img/insert_variant_clang130_x64.png differ diff --git a/doc/img/insert_variant_gcc132_x64.png b/doc/img/insert_variant_gcc132_x64.png new file mode 100644 index 0000000..4ba3c55 Binary files /dev/null and b/doc/img/insert_variant_gcc132_x64.png differ diff --git a/doc/img/insert_variant_vs2022_x64.png b/doc/img/insert_variant_vs2022_x64.png new file mode 100644 index 0000000..4910ad3 Binary files /dev/null and b/doc/img/insert_variant_vs2022_x64.png differ diff --git a/doc/poly_collection.qbk b/doc/poly_collection.qbk index 64795b8..8f71f08 100644 --- a/doc/poly_collection.qbk +++ b/doc/poly_collection.qbk @@ -1,7 +1,7 @@ [library Boost.PolyCollection [quickbook 1.6] [authors [López Muñoz, Joaquín M]] - [copyright 2016-2021 Joaquín M López Muñoz] + [copyright 2016-2024 Joaquín M López Muñoz] [category containers] [id poly_collection] [dirname poly_collection] @@ -17,6 +17,7 @@ [def _Boost.TypeErasure_ [@boost:/libs/type_erasure Boost.TypeErasure]] [def _Boost.TypeIndex_ [@boost:/libs/type_index Boost.TypeIndex]] +[def _Boost.Variant2_ [@boost:/libs/variant2 Boost.Variant2]] [def _CopyConstructible_ [@http://en.cppreference.com/w/cpp/named_req/CopyConstructible [* `CopyConstructible`]]] [def _duck_typing_ [@https://en.wikipedia.org/wiki/Duck_typing ['duck typing]]] [def _EqualityComparable_ [@http://en.cppreference.com/w/cpp/named_req/EqualityComparable [* `EqualityComparable`]]] @@ -25,6 +26,7 @@ [def _MoveAssignable_ [@http://en.cppreference.com/w/cpp/named_req/MoveAssignable [* `MoveAssignable`]]] [def _std::for_each_ [@http://en.cppreference.com/w/cpp/algorithm/for_each `std::for_each`]] [def _std::function_ [@http://en.cppreference.com/w/cpp/utility/functional/function `std::function`]] +[def _std::variant_ [@https://en.cppreference.com/w/cpp/utility/variant `std::variant`]] [def _std::unordered_multiset_ [@http://en.cppreference.com/w/cpp/container/unordered_multiset `std::unordered_multiset`]] [def _RandomAccessIterator_ [@http://en.cppreference.com/w/cpp/named_req/RandomAccessIterator [* `RandomAccessIterator`]]] @@ -50,19 +52,22 @@ lower execution speed. When the particular traversal order is not relevant to the user application, Boost.PolyCollection proposes an alternative data structure that restores memory -contiguity and packs elements according to their concrete type. Three container +contiguity and packs elements according to their concrete type. Four container class templates are provided: * `boost::base_collection` * `boost::function_collection` * `boost::any_collection` +* `boost::variant_collection` -respectively dealing with three different types of dynamic polymorphism +respectively dealing with four different types of dynamic polymorphism available in C++: * Classic base/derived or OOP polymorphism. * Function wrapping in the spirit of _std::function_. * So-called _duck_typing_ as implemented by _Boost.TypeErasure_. +* /Closed polymorphism/ where the list of acceptable types is specified +at compile time as in a _std::variant_. The interface of these containers closely follows that of standard containers. Additionally, the library provides versions of many of the standard library @@ -157,14 +162,24 @@ for C++. Under the hood, duck typing requires pointer indirection and virtual call implementation techniques analogous to those of OOP, and so there are the same opportunities for efficient container data structures as we have described. +* /Closed polymorphism/ is the case where the acceptable types are specified +at compile time. _std::variant_ and _Boost.Variant2_ are prominent examples +of closed polymorphism with underlying types (called /alternatives/) +being accessed through +[@https://en.cppreference.com/w/cpp/utility/variant/visit2 visitation]. +Typical implementations of closed polymorphism do not involve dynamic allocation +(alternatives are stored `union` style in shared storage), but grouping +same-type objects together still provides performance and space advantages. -Boost.PolyCollection provides three different container class templates -dealing with OOP, `std::function`-like polymorphism and duck typing as -implemented by Boost.TypeErasure: +Boost.PolyCollection provides four different container class templates +dealing with OOP, `std::function`-like polymorphism, duck typing as +implemented by Boost.TypeErasure, and closed polymorphism in the spirit of +`std::variant`: * `boost::base_collection` * `boost::function_collection` * `boost::any_collection` +* `boost::variant_collection` The interfaces of these containers are mostly the same and follow the usual conventions of standard library containers. @@ -370,11 +385,55 @@ but a similarly behaving entity `boost::type_erasure::any` for some internally defined `Concept2` that extends `Concept`.]. In any case, we are not accessing sprites through an abstract `sprite&` -anymore, so we could as well dismantle the virtual hierarchy and implement +anymore, so we could in principle dismantle the virtual hierarchy and implement each type autonomously: this is left as an exercise to the reader. [endsect] +[section `boost::variant_collection`] + +[import ../example/basic_variant.cpp] +(Code samples from [@boost:/libs/poly_collection/example/basic_variant.cpp `basic_variant.cpp`]. +C++17 is used for the `overloaded` utility.) + +[endsect] + +At this point where we have such an heterogeneous list of unrelated types, we +may as well specify them at compile time: + +[basic_variant_1] + +`boost::mp11::mp_list` is needed to syntactically distinguish the types that +go in the collection from the (defaulted) allocator argument that comes next. +If no special allocator is specified, the alias template `variant_collection_of` provides +a sligthly more convenient syntax: + +[basic_variant_2] + +`boost::variant_collection` behaves as a `std::vector` of _std::variant_'''s''', +except that objects of the same type are grouped together. Access to the objects +is done through /visitation/: + +[basic_variant_3] + +Output: + +[pre +warrior 1,warrior 4,juggernaut 2,goblin 0,elf 3,"stamina: 10,000","game over",'''[pop-up 1]''','''[pop-up 2]''' +] + +The alternative visitation function `visit_by_index` may be more convenient in that +it doesn't require that we build an overloaded visitor: + +[basic_variant_4] + +Note that types are traversed in the exact same order as specified. The `value_type` +of a `boost::variant_collection>` behaves as a +`std::variant`, with some obvious restrictions such as the impossibility +of changing the type of the contained object (for instance, the equivalent of +[@https://en.cppreference.com/w/cpp/utility/variant/emplace `std::variant::emplace`] +is not provided). + [endsect] [section Deeper into the segmented nature of Boost.PolyCollection] @@ -435,6 +494,12 @@ expected to be much less frequent with `boost::function_collection` and [segmented_structure_4] +[note `boost::variant_collection` handles type registration differently: as the +acceptable types are specified at compile time, their associated segments +are automatically created upon construction of the collection; +consequently, `is_registered` and `register_types` are not provided. +] + [endsect] [section Segment-specific interface] @@ -448,7 +513,12 @@ refer to the elements of a given segment by providing either their type or Note that any of these (except [link poly_collection.tutorial.deeper_into_the_segmented_nature.reserve `reserve`]) will throw `boost::poly_collection::unregistered_type` if the type is not -registered. Segment-specific addressability also includes traversal: +registered. In the case of `boost::variant_collection`, we use the /index number/ of +the type rather than `typeid()`: + +[segmented_structure_6] + +Segment-specific addressability also includes traversal: [endsect] @@ -456,7 +526,7 @@ registered. Segment-specific addressability also includes traversal: The following runs only over the `warrior`s of the collection: -[segmented_structure_6] +[segmented_structure_7] Output: @@ -475,11 +545,11 @@ whole-collection iterators only model _ForwardIterator_. A terser range-based `for` loop can be used with the convenience `segment` member function: -[segmented_structure_7] +[segmented_structure_8] Even more powerful than `local_base_iterator` is `local_iterator`: -[segmented_structure_8] +[segmented_structure_9] This appends a `"super"` prefix to the `rank` data member of existing warriors: @@ -499,6 +569,11 @@ objects. `local_iterator`s can be explicitly converted to to a segment for `T`, it can then be explictly converted to a `local_iterator` (otherwise the conversion is undefined behavior). +The fact that `local_iterator` refers directly to a `T` object is more +apparent when we are using `boost::variant_collection`: + +[segmented_structure_10] + The figure shows the action scopes of all the iterators associated to a polymorphic collection (`const_` versions not included): @@ -510,7 +585,7 @@ of a particular segment, `segment_traversal()` returns an object for ranging ove /segments/, so that the whole collection can be processed with a nested segment-element `for` loop like the following: -[segmented_structure_9] +[segmented_structure_11] Segment decomposition of traversal loops forms the basis of [link poly_collection.tutorial.algorithms improved-performance algorithms]. @@ -522,7 +597,7 @@ Segment decomposition of traversal loops forms the basis of Much like `std::vector`, segments can be made to reserve memory in advance to minimize reallocations: -[segmented_structure_10] +[segmented_structure_12] If there is no segment for the indicated type (here, `goblin`), one is automatically created. This is in contrast with the rest of segment-specific @@ -532,7 +607,7 @@ member functions, which throw `reserve` can deal with all segments at once. The following -[segmented_structure_11] +[segmented_structure_13] instructs every /existing/ segment to reserve 1,000 elements. If a segment is created later (through element insertion or with @@ -710,7 +785,7 @@ signature, etc.]. [import ../example/algorithms.cpp] (Code samples from [@boost:/libs/poly_collection/example/algorithms.cpp `algorithms.cpp`]. -C++14 generic lambda expressions are used.) +C++14 generic lambda expressions are used. C++17 used for the `overloaded` utility.) The ultimate purpose of Boost.PolyCollection is to speed up the processing of large quantities of polymorphic entities, in particular for those operations @@ -725,6 +800,7 @@ Boost.PolyCollection: * `std::vector` (or similar) → `boost::base_collection` * `std::vector>` → `boost::function_collection` * `std::vector>` → `boost::any_collection` +* `std::vector>` → `boost::any_collection>` is expected to increase performance due to better caching and branch prediction behavior. But there is still room for improvement. @@ -853,6 +929,15 @@ capabilities /may/ be able to figure out that the type restitution scenario is actually casting the element to its true type, in which case, again, virtual calls are not needed. +When using `boost::variant_collection`, the convenience marker `all_types` +requests that all types be restituted without having to explicitly +write them down. This is of course feasible because these types are, again, +known at compile time. Note that, since elements are accessed directly according +to their type, no `visit` call is made in the example +[footnote Visitation avoidance is in fact the equivalent thing to devirtualization +in the realm of closed polymorphism.]. +[algorithms_9] + [endsect] [endsect] @@ -861,10 +946,9 @@ calls are not needed. [section Performance] -[def _vs2015_x86_ Visual Studio 2015 x86] -[def _vs2015_x64_ Visual Studio 2015 x64] -[def _gcc63_x64_ GCC 6.3 x64] -[def _clang40_x64_ Clang 4.0 x64] +[def _vs2022_x64_ Visual Studio 2022 x64] +[def _gcc132_x64_ GCC 13.2 x64] +[def _clang130_x64_ Clang 13.0 x64] [template perf_fig[name environment file] [section [environment]] @@ -891,13 +975,13 @@ Boost.PolyCollection in two scenarios: [link poly_collection.tutorial.algorithms.type_restitution type restitution]). As a comparison baseline we used containers and facilities from the -standard library and Boost (details below). Tests were run in the -following environments: +standard library and Boost (details below). Tests were run on a Windows 10 machine +with 8GB RAM and an Intel Core i5-8265U CPU @1.60GHz (base frequency) +for the following environments: -* *_vs2015_x86_*: Visual C++ 2015 in 32-bit (x86) release mode, Windows 7 64-bit, Intel Core i5-2520M @2.5GHz -* *_vs2015_x64_*: Visual C++ 2015 in 64-bit (x64) release mode, same machine -* *_gcc63_x64_*: GCC 6.3 release mode, Xubuntu 17.04 x64, Intel Core i7-5820 @3.3GHz -* *_clang40_x64_*: Clang 4.0 release mode, same machine +* *_vs2022_x64_*: Visual Studio 2022 in 64-bit (x64), release mode +* *_gcc132_x64_*: GCC 13.2.0 for MinGW, `-O3 -DNDEBUG` +* *_clang130_x64_*: Clang 13.0.1 for Visual Studio (clang-cl) in 64-bit (x64), release mode [section Container definitions] @@ -931,39 +1015,48 @@ following environments: [endsect] +[section `boost::variant_collection`] + +* Baseline container: `variant_vector` = `std::vector>` +* Polymorphic collection: `variant_collection` = `boost::variant_collection>` +* Element types: `T1` = `int`, `T2` = `double`, `T3` = `char` + +[endsect] + [endsect] [section Insertion tests] Tests measure the time taken to insert /n/ elements (/n/ between 10'''2''' and 10'''7''') -from a source of values with types following the cyclic sequence - -`T1` `T1` `T2` `T2` `T3` `T1` `T1` `T2` `T2` `T3` ··· +from a source of values with types randomly selected from `T1`, `T2` and `T3` with equal probability. No `reserve` operation is done before insertion. The figures show resulting times in nanoseconds/element. The horizontal axis is logarithmic. [section Results for `boost::base_collection`] -[perf_fig Insertion.._vs2015_x86_..insert_base_vs2015_x86] -[perf_fig Insertion.._vs2015_x64_..insert_base_vs2015_x64] -[perf_fig Insertion.._gcc63_x64_..insert_base_gcc63_x64] -[perf_fig Insertion.._clang40_x64_..insert_base_clang40_x64] +[perf_fig Insertion.._vs2022_x64_..insert_base_vs2022_x64] +[perf_fig Insertion.._gcc132_x64_..insert_base_gcc132_x64] +[perf_fig Insertion.._clang130_x64_..insert_base_clang130_x64] [endsect] [section Results for `boost::function_collection`] -[perf_fig Insertion.._vs2015_x86_..insert_function_vs2015_x86] -[perf_fig Insertion.._vs2015_x64_..insert_function_vs2015_x64] -[perf_fig Insertion.._gcc63_x64_..insert_function_gcc63_x64] -[perf_fig Insertion.._clang40_x64_..insert_function_clang40_x64] +[perf_fig Insertion.._vs2022_x64_..insert_function_vs2022_x64] +[perf_fig Insertion.._gcc132_x64_..insert_function_gcc132_x64] +[perf_fig Insertion.._clang130_x64_..insert_function_clang130_x64] [endsect] [section Results for `boost::any_collection`] -[perf_fig Insertion.._vs2015_x86_..insert_any_vs2015_x86] -[perf_fig Insertion.._vs2015_x64_..insert_any_vs2015_x64] -[perf_fig Insertion.._gcc63_x64_..insert_any_gcc63_x64] -[perf_fig Insertion.._clang40_x64_..insert_any_clang40_x64] +[perf_fig Insertion.._vs2022_x64_..insert_any_vs2022_x64] +[perf_fig Insertion.._gcc132_x64_..insert_any_gcc132_x64] +[perf_fig Insertion.._clang130_x64_..insert_any_clang130_x64] +[endsect] + +[section Results for `boost::variant_collection`] +[perf_fig Insertion.._vs2022_x64_..insert_variant_vs2022_x64] +[perf_fig Insertion.._gcc132_x64_..insert_variant_gcc132_x64] +[perf_fig Insertion.._clang130_x64_..insert_variant_clang130_x64] [endsect] [endsect] @@ -983,21 +1076,25 @@ whereas for `boost::any_collection` we use [perf_for_each_incrementable] +and for `boost::variant_collection` we have + +[perf_for_each_alternative] + The baseline container is tested with three different setups: * Directly as initialized by the process described for the [link poly_collection.performance.insertion_tests insertion tests]. -The sequence of types is complex enough that CPU's branch prediction mechanisms -are not able to fully anticipate it -[footnote This has been verified empirically: simpler cycles did indeed -yield better execution times.]. As elements are ordered according to their +As elements are ordered according to their construction time, certain degree of memory contiguity is expected. * With an extra post-insertion stage by which elements are sorted -according to their `typeid()`. This increases branch prediction +according to their type. This increases branch prediction efficiency at the expense of having worse cache friendliness. * With an extra post-insertion stage that randomly shuffles all the elements in the container. This is the worst possible scenario both in terms of caching and branch prediction. +We don't exercise this setup for `variant_vector` as it is +at equivalent to the direct case (`std::variant` does not allocate +dynamic memory). As for the polymorphic collection, three variations are measured: @@ -1011,24 +1108,27 @@ The figures show resulting times in nanoseconds/element. The horizontal axis is logarithmic. [section Results for `boost::base_collection`] -[perf_fig Processing.._vs2015_x86_..for_each_base_vs2015_x86] -[perf_fig Processing.._vs2015_x64_..for_each_base_vs2015_x64] -[perf_fig Processing.._gcc63_x64_..for_each_base_gcc63_x64] -[perf_fig Processing.._clang40_x64_..for_each_base_clang40_x64] +[perf_fig Processing.._vs2022_x64_..for_each_base_vs2022_x64] +[perf_fig Processing.._gcc132_x64_..for_each_base_gcc132_x64] +[perf_fig Processing.._clang130_x64_..for_each_base_clang130_x64] [endsect] [section Results for `boost::function_collection`] -[perf_fig Processing.._vs2015_x86_..for_each_function_vs2015_x86] -[perf_fig Processing.._vs2015_x64_..for_each_function_vs2015_x64] -[perf_fig Processing.._gcc63_x64_..for_each_function_gcc63_x64] -[perf_fig Processing.._clang40_x64_..for_each_function_clang40_x64] +[perf_fig Processing.._vs2022_x64_..for_each_function_vs2022_x64] +[perf_fig Processing.._gcc132_x64_..for_each_function_gcc132_x64] +[perf_fig Processing.._clang130_x64_..for_each_function_clang130_x64] [endsect] [section Results for `boost::any_collection`] -[perf_fig Processing.._vs2015_x86_..for_each_any_vs2015_x86] -[perf_fig Processing.._vs2015_x64_..for_each_any_vs2015_x64] -[perf_fig Processing.._gcc63_x64_..for_each_any_gcc63_x64] -[perf_fig Processing.._clang40_x64_..for_each_any_clang40_x64] +[perf_fig Processing.._vs2022_x64_..for_each_any_vs2022_x64] +[perf_fig Processing.._gcc132_x64_..for_each_any_gcc132_x64] +[perf_fig Processing.._clang130_x64_..for_each_any_clang130_x64] +[endsect] + +[section Results for `boost::variant_collection`] +[perf_fig Processing.._vs2022_x64_..for_each_variant_vs2022_x64] +[perf_fig Processing.._gcc132_x64_..for_each_variant_gcc132_x64] +[perf_fig Processing.._clang130_x64_..for_each_variant_clang130_x64] [endsect] [endsect] @@ -1089,24 +1189,6 @@ parallel processing. [endsect] -[section `variant_collection`] - -/Closed polymorphism/ is a kind of dynamic polymorphism where the set of implementation -types is fixed at definition time: the prime example of this paradigm in C++ is -[@http://en.cppreference.com/w/cpp/utility/variant `std::variant`]. Although -`boost::any_collection>` can act as a sort of replacement for -`std::vector>`, this is in fact more similar to a -`std::vector<`[@http://en.cppreference.com/w/cpp/utility/any `std::any`]`>`, -and a collection class `boost::variant_collection` could be designed to -better model closed polymorphism and take further advantage of the fact that -implementation types are fixed (for instance, internal virtual calls can be -completely eliminated). From a conceptual point of view, this would require introducing -a new [* `ClosedPolymorphicCollection`] notion and renaming the current -[link poly_collection.reference.polymorphic_containers.polymorphic_collections [* `PolymorphicCollection`]] -model to [* `OpenPolymorphicCollection`]. - -[endsect] - [section Ordered polymorphic collections] Users have expressed interest in polymorphic collections where elements are kept @@ -1122,6 +1204,13 @@ to allow for the definition of container-level `operator<` and related operators [section Release notes] +[section Boost 1.88] + +* Added `boost::variant_collection`, a closed polymorphic collection +similar in behavior to `std::vector>`. + +[endsect] + [section Boost 1.76] * Worked around [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95888 GCC bug] affecting @@ -1203,7 +1292,7 @@ Boost.PolyCollection: [endsect] -[section Acknowledgments] +[section Acknowledgements] The library uses [@http://www.pdimov.com/ Peter Dimov]'s [@http://www.pdimov.com/cpp2/simple_cxx11_metaprogramming.html implementation @@ -1223,7 +1312,7 @@ Adam Wulkiewicz. Many thanks to all of them. Steven Watanabe gave crucial help i solving some hard problems related to the usage of _Boost.TypeErasure_. Boost.PolyCollection was designed and written between rainy -[@https://es.wikipedia.org/wiki/Viav%C3%A9lez Viavélez], noisy Madrid and beautiful +[@https://es.wikipedia.org/wiki/Viav%C3%A9lez_(Valdepares) Viavélez], noisy Madrid and beautiful [@https://en.wikipedia.org/wiki/C%C3%A1ceres,_Spain Cáceres], August-November, 2016. Most of the after-review work in preparation for the official Boost release was done in the quiet town of [@https://es.wikipedia.org/wiki/Oropesa_(Toledo) Oropesa] during diff --git a/doc/poly_collection_synopsis.qbk b/doc/poly_collection_synopsis.qbk index 6bcd862..c6d83f3 100644 --- a/doc/poly_collection_synopsis.qbk +++ b/doc/poly_collection_synopsis.qbk @@ -1,11 +1,11 @@ [/ - Copyright 2016-2017 Joaquin M Lopez Munoz. + Copyright 2016-2024 Joaquin M Lopez Munoz. Distributed under 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) ] -[template poly_collection_synopsis[class_name template_header value_type] +[template poly_collection_synopsis[class_name template_header value_type type_index] ``[template_header]`` class ``[class_name]`` { @@ -20,6 +20,7 @@ using const_reference=const value_type&; using pointer=typename std::allocator_traits::pointer; using const_pointer=typename std::allocator_traits::const_pointer; + using _type_index_=``[type_index]``; using iterator=``/implementation-defined/``; using const_iterator=``/implementation-defined/``; using _local_base_iterator_=``/implementation-defined/``; @@ -58,7 +59,7 @@ template void _register_types_(); - bool _is_registered_(const std::type_info& info)const; + bool _is_registered_(const type_index& info)const; template bool _is_registered_()const; ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.iterators ['// iterators:]]`` @@ -70,12 +71,12 @@ const_iterator _cbegin_()const noexcept; const_iterator _cend_()const noexcept; - local_base_iterator _begin_(const std::type_info& info); - local_base_iterator _end_(const std::type_info& info); - const_local_base_iterator _begin_(const std::type_info& info)const; - const_local_base_iterator _end_(const std::type_info& info)const; - const_local_base_iterator _cbegin_(const std::type_info& info)const; - const_local_base_iterator _cend_(const std::type_info& info)const; + local_base_iterator _begin_(const type_index& info); + local_base_iterator _end_(const type_index& info); + const_local_base_iterator _begin_(const type_index& info)const; + const_local_base_iterator _end_(const type_index& info)const; + const_local_base_iterator _cbegin_(const type_index& info)const; + const_local_base_iterator _cend_(const type_index& info)const; template local_iterator _begin_(); template local_iterator _end_(); @@ -84,8 +85,8 @@ template const_local_iterator _cbegin_()const; template const_local_iterator _cend_()const; - base_segment_info _segment_(const std::type_info& info); - const_base_segment_info _segment_(const std::type_info& info)const; + base_segment_info _segment_(const type_index& info); + const_base_segment_info _segment_(const type_index& info)const; template segment_info _segment_(); template const_segment_info _segment_()const; @@ -95,25 +96,25 @@ ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.capacity ['// capacity:]]`` bool empty()const noexcept; - bool _empty_(const std::type_info& info)const; + bool _empty_(const type_index& info)const; template bool _empty_()const; size_type size()const noexcept; - size_type _size_(const std::type_info& info)const; + size_type _size_(const type_index& info)const; template size_type _size_()const; - size_type _max_size_(const std::type_info& info)const; + size_type _max_size_(const type_index& info)const; template size_type _max_size_()const; - size_type _capacity_(const std::type_info& info)const; + size_type _capacity_(const type_index& info)const; template size_type _capacity_()const; void _reserve_(size_type n); - void _reserve_(const std::type_info& info,size_type n); + void _reserve_(const type_index& info,size_type n); templatevoid _reserve_(size_type n); void _shrink_to_fit_(); - void _shrink_to_fit_(const std::type_info& info); + void _shrink_to_fit_(const type_index& info); template void _shrink_to_fit_(); ``[link poly_collection.reference.polymorphic_containers.polymorphic_collections.modifiers ['// modifiers:]]`` @@ -146,7 +147,7 @@ auto _erase_(CollectionIterator first,CollectionIterator last); void _clear_()noexcept; - void _clear_(const std::type_info& info); + void _clear_(const type_index& info); template void _clear_(); void swap(``[class_name]``& x); diff --git a/doc/reference.qbk b/doc/reference.qbk index 8857255..872a98c 100644 --- a/doc/reference.qbk +++ b/doc/reference.qbk @@ -1,5 +1,5 @@ [/ - Copyright 2016-2017 Joaquin M Lopez Munoz. + Copyright 2016-2024 Joaquin M Lopez Munoz. Distributed under 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) @@ -46,6 +46,11 @@ Static polymorphism is the trivial case where *subobject*(`x`) = `x` for all * *Implementation*(`Base`) = { `Derived` : `std::is_base_of_v` }. * *subobject*(`x`) = `static_cast(x)` with `typeid(x)==typeid(Derived)`. +A polymorphism model is called /closed/ if, for any `I` \u2208 *Interface*, +*suboject* ranges over a finite subset ot types of *Implementation*(`I`), +that is, there is only a finite number of types subojects can belong in. +A non-closed model is called /open/. + [endsect] [section Polymorphic containers] @@ -91,9 +96,11 @@ where a type `U` is said to be acceptable if [@http://en.cppreference.com/w/cpp/types/is_move_constructible `std::is_nothrow_move_constructible::value`] is `true`. +The collection is /open/ (resp. /closed/) if its associated polymorphism model +is open (resp. closed). Polymorphic collections conform to the requirements of _PolymorphicContainer_ with the following -modfications and extra guarantees: +modifications and extra guarantees: * The complexity of `empty()` and `size()` is linear on the number of segments of the collection. @@ -107,7 +114,8 @@ _CopyAssignable_, _MoveConstructible_ or _MoveAssignable_. A type `U` is said to be /registered/ into the collection if a (possibly empty) segment for `U` has been created. Registered types continue to stay so for the duration of the container except if it is -moved from, assigned to, or swapped. +moved from, assigned to, or swapped. For closed collections, all +acceptable types are automatically registered upon construction. Each segment has an associated capacity indicating the maximum size that it can attain without reallocation. When the limit @@ -115,8 +123,10 @@ is exceeded (or explicitly through `reserve`) new storage space is allocated with greater capacity and elements are moved. Collection traversal goes through the elements of the first segment, -then the second, etc. The order in which segments are visited is -unspecified but remains stable until a new segment is created. +then the second, etc. For open collections, the order in which segments +are visited is unspecified but remains stable until a new segment is created. +For closed collections, segment order is fixed and determined by +the specification of the collection type. Besides `iterator` and `const_iterator`, there are iterator types `local_base_iterator` and `local_iterator` (and their `const_` @@ -144,7 +154,7 @@ we use the following notation: * `C` is a polymorphic collection type, * `c` is an object of type `C`, `cc` is a possibly `const` object of type `C`, * `al` is a value of type `C::allocator_type`, -* `info` is a `const std::type_info&`, +* `info` is a value of type `const C::index_type&`, * `U` is an acceptable type, `Us...` is a template parameter pack of acceptable types, * `n` is a value of `size_type`, @@ -169,6 +179,18 @@ local) of `c` such that \[`xit1`, `xit2`) is a valid range. [section Types] +[#poly_collection.reference.polymorphic_containers.polymorphic_collections.types.type_index] +[def _type_index_ [link poly_collection.reference.polymorphic_containers.polymorphic_collections.types.type_index `type_index`]] + +`C::type_index` + +Can be either `std::type_info` or `std::size_t`. We define `index` as follows: + +* if `type_index` is `std::type_info`: `typeid(U)`, +* if `type_index` is `std::size_t`: the index number `i` such that `U` is the `i`-th +element in `Us...`, where `Us...` is the list of acceptable types as specified in the +associated polymorphism model. + [#poly_collection.reference.polymorphic_containers.polymorphic_collections.types.local_base_iterator] [def _local_base_iterator_ [link poly_collection.reference.polymorphic_containers.polymorphic_collections.types.local_base_iterator `local_base_iterator`]] @@ -219,15 +241,15 @@ _CopyConstructible_ and _CopyAssignable_ type with information about a given segment of a collection. If `ci` is a possibly `const` object of type `C::const_base_segment_info` associated to the segment of `c` for `U`, then -* `ci.begin()==c.cbegin(typeid(U))` -* `ci.cbegin()==c.cbegin(typeid(U))` +* `ci.begin()==c.cbegin(index)` +* `ci.cbegin()==c.cbegin(index)` * `ci.begin()==c.cbegin()` * `ci.cbegin()==c.cbegin()` -* `ci.end()==c.cend(typeid(U))` -* `ci.cend()==c.cend(typeid(U))` +* `ci.end()==c.cend(index)` +* `ci.cend()==c.cend(index)` * `ci.end()==c.cend()` * `ci.cend()==c.cend()` -* `ci.type_info()==typeid(U)` +* `ci.type_info()==index` [#poly_collection.reference.polymorphic_containers.polymorphic_collections.types.base_segment_info] [def _base_segment_info_ [link poly_collection.reference.polymorphic_containers.polymorphic_collections.types.base_segment_info `base_segment_info`]] @@ -239,9 +261,9 @@ _CopyConstructible_ and _CopyAssignable_ type publicly derived from if `i` is an object of type `C::base_segment_info` associated to the segment of `c` for `U`, then -* `i.begin()==c.begin(typeid(U))` +* `i.begin()==c.begin(index)` * `i.begin()==c.begin()` -* `i.end()==c.end(typeid(U))` +* `i.end()==c.end(index)` * `i.end()==c.end()` [#poly_collection.reference.polymorphic_containers.polymorphic_collections.types.const_segment_info] @@ -330,7 +352,7 @@ Internally calls `this->insert(j1,j2)` on construction. [endsect] -[section Type registration] +[section:type_registration Type registration (open collections only)] [#poly_collection.reference.polymorphic_containers.polymorphic_collections.type_registration.register_types] [def _register_types_ [link poly_collection.reference.polymorphic_containers.polymorphic_collections.type_registration.register_types `register_types`]] @@ -646,6 +668,7 @@ from the last one to the end of its segment. [endsect] [import poly_collection_synopsis.qbk] [/ template poly_collection_synopsis] +[import closed_poly_collection_synopsis.qbk] [/ template closed_poly_collection_synopsis] [section Header `"boost/poly_collection/exception.hpp"` synopsis] @@ -794,14 +817,14 @@ namespace. [section Class template `base_collection`] -`base_collection` is a _PolymorphicCollection_ associated to +`base_collection` is an open _PolymorphicCollection_ associated to the classic base/derived _polymorphism_model_: * *Interface* = { `Base` : `std::is_polymorphic_v` }. * *Implementation*(`Base`) = { `Derived` : `std::is_base_of_v` }. * *subobject*(`x`) = `static_cast(x)` with `typeid(x)==typeid(Derived)`. -[poly_collection_synopsis `base_collection`..`template`..`Base`] +[poly_collection_synopsis `base_collection`..`template`..`Base`..`std::type_info`] [endsect] @@ -974,19 +997,16 @@ wrapped callable object. [section Class template `function_collection`] -`function_collection` is a _PolymorphicCollection_ associated to +`function_collection` is an open _PolymorphicCollection_ associated to a dynamic _polymorphism_model_ based on call signature compatibility: -[itemized_list - [*Interface* = { `Signature` : `Signature` = `R(Args...)` }.] - [*Implementation*(`Signature`) = { `Callable` : `std::is_invocable_r_v` }.] - [*subobject*(`x`) =[br] - `x.target()` with `typeid(T)==x.target_type()`, if `x` is an instantiation of _function_collection_value_type_,[br] - `x`, otherwise. - ] -] +* *Interface* = { `Signature` : `Signature` = `R(Args...)` }. +* *Implementation*(`Signature`) = { `Callable` : `std::is_invocable_r_v` }. +* *subobject*(`x`) = + * `x.target()` with `typeid(T)==x.target_type()`, if `x` is an instantiation of _function_collection_value_type_, + * `x`, otherwise. -[poly_collection_synopsis `function_collection`..`template`..`_function_collection_value_type_`] +[poly_collection_synopsis `function_collection`..`template`..`_function_collection_value_type_`..`std::type_info`] [endsect] @@ -1093,26 +1113,405 @@ The exact definition of `Concept2` is implementation defined. [section Class template `any_collection`] -`any_collection` is a _PolymorphicCollection_ associated to +`any_collection` is an open _PolymorphicCollection_ associated to a dynamic _polymorphism_model_ based on _duck_typing_ as implemented by _Boost.TypeErasure_: -[itemized_list - [*Interface* = { `Concept` : +* *Interface* = { `Concept` : as [@boost:/doc/html/boost_typeerasure/conceptdef.html specified] by _Boost.TypeErasure_, using the [@boost:/doc/html/boost/type_erasure/_self.html `_self`] - [@boost:/doc/html/boost/type_erasure/placeholder.html placeholder] }.] - [*Implementation*(`Concept`) = { `Concrete` : `Concrete` satisfies `Concept` }.] - [*subobject*(`x`) =[br] - `boost::type_erasure::`[@boost:/doc/html/boost/type_erasure/any_cast.html `any_cast`]`(x)` + [@boost:/doc/html/boost/type_erasure/placeholder.html placeholder] }. +* *Implementation*(`Concept`) = { `Concrete` : `Concrete` satisfies `Concept` }. +* *subobject*(`x`) = + * `boost::type_erasure::`[@boost:/doc/html/boost/type_erasure/any_cast.html `any_cast`]`(x)` with `typeid(T)==boost::type_erasure::`[@boost:/doc/html/boost/type_erasure/typeid_of.html `typeid_of`]`(x)`, if `x` is an instantiation of `boost::type_erasure::`[@boost:/doc/html/boost/type_erasure/any.html `any`] - including [@boost:/doc/html/boost/type_erasure/typeid_.html `typeid_`]`<>`,[br] - `x`, otherwise. - ] -] + including [@boost:/doc/html/boost/type_erasure/typeid_.html `typeid_`]`<>`, + * `x`, otherwise. -[poly_collection_synopsis `any_collection`..`template`..`_any_collection_value_type_`] +[poly_collection_synopsis `any_collection`..`template`..`_any_collection_value_type_`..`std::type_info`] + +[endsect] + +[endsect] + +[section Header `"boost/poly_collection/variant_collection_fwd.hpp"` synopsis] + +[def _variant_collection_ [link poly_collection.reference.header_boost_poly_collection_va0.class_template_variant_collectio `variant_collection`]] +[def _variant_collection_value_type_ [link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio `variant_collection_value_type`]] + + #include + #include + + namespace boost{ + + namespace poly_collection{ + + template + using _variant_collection_value_type_=``/implementation-defined/``; + + template< + typename TypeList, + typename Allocator=std::allocator> + > + class _variant_collection_; + + template + using variant_collection_of=variant_collection>; + + template + bool operator==( + const variant_collection& x, + const variant_collection& y); + + template + bool operator!=( + const variant_collection& x, + const variant_collection& y); + + template + void swap( + variant_collection& x,variant_collection& y); + + } /* namespace poly_collection */ + + using poly_collection::variant_collection; + using poly_collection::variant_collection_of; + + } /* namespace boost */ + +Defines the alias template _variant_collection_value_type_ (the actual type +it refers to, though, is merely forward declared). +Forward declares the class template _variant_collection_ +and specifies its default template arguments. +Defines the alias template `variant_collection_of`. Forward declares associated +free functions and brings `boost::poly_collection::variant_collection` +and `poly_collection::variant_collection_of` to the `boost` namespace. + +[endsect] + +[section Header `"boost/poly_collection/variant_collection.hpp"` synopsis] + + #include + + namespace boost{ + + namespace poly_collection{ + + // defines the type ``_variant_collection_value_type_`` refers to + + template + class _variant_collection_; + + template + bool operator==( + const variant_collection& x, + const variant_collection& y); + + template + bool operator!=( + const variant_collection& x, + const variant_collection& y); + + template + void swap( + variant_collection& x,variant_collection& y); + + } /* namespace poly_collection */ + + } /* namespace boost */ + +[section Alias template `variant_collection_value_type`] + +`variant_collection_value_type` is the `value_type` of +`boost::variant_collection`, where `TypeList` +is a [@boost:/libs/mp11/doc/html/mp11.html#definitions Boost.Mp11 list] +of different types `Ts...`. +`variant_collection_value_type` behaves similarly to +_std::variant_``, with some intentional limitations such as the impossibility +of changing the type of the underlying alternative. + +In what follows, the name [' `variant_collection_value_type_impl`] is used +just for explanatory purposes in place of the actual class template name, +which is implementation defined. + + template + using variant_collection_value_type= + mp11::mp_rename; + + template + class ``/variant_collection_value_type_impl/`` + { + public: + std::size_t ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.index index]``()const noexcept; + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.valueless_by_exception valueless_by_exception]``()const noexcept; + }; + + struct bad_variant_access:std::exception + { + bad_variant_access()noexcept{} + const char* what()const noexcept; + }; + + template struct variant_size; // not defined + template + struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_size variant_size]``<``/variant_collection_value_type_impl/``>; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_size variant_size]``; + + template + constexpr std::size_t variant_size_v=variant_size::value; + + template struct variant_alternative; // not defined + template + struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``>; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``; + template struct ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative variant_alternative]``; + + template + using variant_alternative_t=typename variant_alternative::type; + + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.holds_alternative holds_alternative]``(const ``/variant_collection_value_type_impl/``& x)noexcept; + + template + variant_alternative_t ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_by_index get]``(V&& x); + + template + variant_alternative_t,T>::value,V&&> ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_by_type get]``(V&& x); + + template + variant_alternative_t>* + ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_index get_if]``(``/variant_collection_value_type_impl/``* px)noexcept; + template + const variant_alternative_t>* + ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_index get_if]``(const ``/variant_collection_value_type_impl/``* px)noexcept; + + template + T* ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_type get_if]``(``/variant_collection_value_type_impl/``* px)noexcept; + template + const T* ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_type get_if]``(const ``/variant_collection_value_type_impl/``* px)noexcept; + + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_eq operator==]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_ne operator!=]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_lt operator<]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_gt operator>]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_le operator=<]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + template + bool ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_ge operator>=]``( + const ``/variant_collection_value_type_impl/``& x, + const ``/variant_collection_value_type_impl/``& y); + + template + ``/see-below/`` ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.visit visit]``(F&& f,Vs&&... xs); + + template + ``/see-below/`` ``[link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.visit_by_index visit_by_index]``(V&& x,Fs&&... fs); + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.index] + +`std::size_t index()const noexcept;` + +[*Returns:] The index number into `Ts...` of the type of the alternative +contained in the variant. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.valueless_by_exception] + +`bool valueless_by_exception()const noexcept;` + +[*Returns:] `false`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_size] + +`template`[br] +`struct variant_size<`[^ /variant_collection_value_type_impl/]`>;`[br] +`template struct variant_size;` + +`variant_size<...>::value` is a `constexpr std::size_t` value with +the number of alternative types specified for the variant. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.variant_alternative] + +`template`[br] +`struct variant_alternative>;`[br] +`template struct variant_alternative;`[br] +`template struct variant_alternative;`[br] +`template struct variant_alternative;`[br] +`template struct variant_alternative;`[br] +`template struct variant_alternative;` + +`variant_alternative::type` is the `I`-th alternative type in the variant +with the same `const` and reference qualification as `V`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.holds_alternative] + +`template`[br] +`bool holds_alternative(const `[^ /variant_collection_value_type_impl/]`& x)noexcept;` + +[*Returns:] `true` iff the type of the alternative contained in the variant is `T`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_by_index] + +`template`[br] +`variant_alternative_t get(V&& x);` + +[*Effects:] Throws `bad_variant_access` if `x.index()!=I`.[br] +[*Returns:] A reference to the contained value +with the same `const` and reference qualification as `x`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_by_type] + +`template`[br] +`variant_alternative_t,T>::value,V&&> get(V&& x);` + +[*Effects:] Throws `bad_variant_access` if `!holds_alternative(x)`.[br] +[*Returns:] A reference to the contained value +with the same `const` and reference qualification as `x`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_index] + +`template`[br] +`variant_alternative_t>*`[br] +`get_if(`[^ /variant_collection_value_type_impl/]`* px)noexcept;`[br] +`template`[br] +`const variant_alternative_t>*`[br] +`get_if(const `[^ /variant_collection_value_type_impl/]`* px)noexcept;` + +[*Returns:] `nullptr` if `px` is null or `px->index()!=I`, +a pointer to the contained value otherwise. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.get_if_by_type] + +`template`[br] +`T* get_if(`[^ /variant_collection_value_type_impl/]`* px)noexcept;`[br] +`template`[br] +`const T* get_if(const `[^ /variant_collection_value_type_impl/]`* px)noexcept;` + +[*Returns:] `nullptr` if `px` is null or `!holds_alternative(*px)`, +a pointer to the contained value otherwise. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_eq] + +`template`[br] +`bool operator==(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()==y.index()&&get(x)==get(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_ne] + +`template`[br] +`bool operator!=(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()!=y.index()||get(x)!=get(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_lt] + +`template`[br] +`bool operator<(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()(x)(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_gt] + +`template`[br] +`bool operator>(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()>y.index()||x.index()==y.index()&&get(x)>get(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_le] + +`template`[br] +`bool operator<=(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()(x)<=get(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.operator_ge] + +`template`[br] +`bool operator>=(`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& x,`[br] +[^ \u00a0\u00a0]`const `[^ /variant_collection_value_type_impl/]`& y);` + +[*Returns:] `x.index()>y.index()||x.index()==y.index()&&get(x)>=get(y)` with `I==x.index()`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.visit] + +`template`[br] +[^ /see-below/]` visit(F&& f,Vs&&... xs);` + +[*Effects:] Calls the expression `e(Is...)`, defined as +`std::forward(f)(get(std::forward(xs)...)`, with `Is...` equal to `xs.index()...`.[br] +[*Returns:] + +* If `R` is specified as `void`: nothing. +* If `R` is specified as a type different from `void`: the value `e(Is...)` implicitly +converted to `R`. +* If `R` is not specified, `e(Is...)`. In this and the previous case, +`e(Js...)` must have the same type for all valid index packs `Js...`. + +[#poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio.visit_by_index] + +`template`[br] +[^ /see-below/]` visit_by_index(V&& x,Fs&&... fs);` + +[*Requires:] `sizeof...(Fs)` is equal to the number of alternative types in the variant.[br] +[*Effects:] Calls the expression `e(i)`, defined as +`std::forward(fi)(get(std::forward(x)))`, with `i==x.index()` and +`Fi` and `fi` denoting the `i`-th element of `Fs...` and `fs...`, respectively.[br] +[*Returns:] + +* If `R` is specified as `void`: nothing. +* If `R` is specified as a type different from `void`: the value `e(i)` implicitly +converted to `R`. +* If `R` is not specified, `e(i)`. In this and the previous case, +`e(j)` must have the same type for all `j` in \[0, `sizeof...(Fs)`). + +[endsect] + +[section Class template `variant_collection`] + +`variant_collection` is a closed _PolymorphicCollection_ associated to +a dynamic _polymorphism_model_ based on the interface of _std::variant_: + +* *Interface* = { `TypeList` = `L` : `L` is an arbitrary class template, `Ts...` is not empty, and + each `Ti` in `Ts...` is different, _MoveConstructible_, and _MoveAssignable_ or such that + [@http://en.cppreference.com/w/cpp/types/is_move_constructible `std::is_nothrow_move_constructible::value`] is `true` }. +* *Implementation*(`L`) = { `Ts...` } \u222a + { [link poly_collection.reference.header_boost_poly_collection_va0.alias_template_variant_collectio `variant_collection_value_type`]`` : `Us...` \u2286 `Ts...` } \u222a + { _std::variant_`` : `Us...` \u2286 `Ts...` } \u222a + { [@boost:libs/variant2/doc/html/variant2.html#ref_variant `boost::variant2::variant`]`` : `Us...` \u2286 `Ts...` }. +* *subobject*(`x`) = + * `get(x)` with `I==x.index()`, if the type of `x` is not in `Ts...`, + * `x`, otherwise. + +[closed_poly_collection_synopsis `variant_collection`..`template`..`_variant_collection_value_type_`..`std::size_t`] [endsect] @@ -1124,6 +1523,10 @@ _Boost.TypeErasure_: namespace poly_collection{ + ``['`// total restitution for closed collections:`]`` + + struct all_types; + ``['`// non-modifying sequence operations:`]`` template @@ -1468,12 +1871,15 @@ these algorithms for /type restitution/. For the description of the algorithms we use the following notation: * [' `alg`] is the (unqualified) name of any of the algorithms in -`"boost/poly_collection/algorithm.hpp"` except `copy_n` and `rotate_copy`. +`"boost/poly_collection/algorithm.hpp"` except `for_each_n`, `copy_n` and `rotate_copy`. * `first`, `middle` and `last` are (same-typed) possibly const global iterators of a collection of Boost.PolyCollection such that \[`first`, `middle`) and \[`middle`, `last`) are valid ranges. * `args...` is a function parameter pack of types `Args&&...`, -* `Ts...` is a template parameter pack of arbitrary types. +* `Ts...` is a template parameter pack of arbitrary types. + * Open collections: `Us...` is defined as `Ts...`. + * Closed collections: `Us...` is defined as the set of all acceptable types + in the collection if `all_types` is in `Ts...`, or as `Ts...` otherwise. (1) [' `alg`]`(first,last,args...)`[br] (2) `for_each_n(first,args...)`[br] @@ -1503,14 +1909,18 @@ as:[br] (4) `std::rotate_copy(rfirst,rmiddle,rlast,args...)`,[br] and `rfirst`, `rmiddle` and `rlast` are iterator-like objects behaving like their `first`, `middle` and `last` counterparts except that they -dereference to the corresponding subobject (`const`) `T&` if pointing to a -segment for `T` and `T` is in `Ts...` +dereference to the corresponding subobject (`const`) `U&` if pointing to a +segment for `U` and `U` is in `Us...` [footnote Strictly speaking a proper _ForwardIterator_ cannot behave like this as dereferencing must yield /exactly/ a (`const`) `value_type&` value, which disallows this type of polymorphism.].[br] [*Effects:] Equivalent to `expr`.[br] [*Returns:] `expr`.[br] -[*Complexity:] That of `expr`. +[*Complexity:] That of `expr`.[br] +[*Note:] In the case of closed collections, if `Us...` contains all the +acceptable types of the collection (/total restitution/), then +the dereference operation of `rfirst`, `rmiddle` and `rlast` to the +collection's `value_type` is not ever instantiated. [endsect] diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 index c43008c..f6244e3 100644 --- a/example/Jamfile.v2 +++ b/example/Jamfile.v2 @@ -1,4 +1,4 @@ -# Copyright 2016-2017 Joaquín M López Muñoz. +# Copyright 2016-2024 Joaquín M López Muñoz. # Distributed under 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) @@ -13,7 +13,7 @@ project exe algorithms : algorithms.cpp - : 14 + : 17 ; exe basic_any @@ -29,6 +29,11 @@ exe basic_function : 14 ; +exe basic_variant + : basic_variant.cpp + : 17 + ; + exe exceptions : exceptions.cpp ; diff --git a/example/algorithms.cpp b/example/algorithms.cpp index 30a7e09..5dacd03 100644 --- a/example/algorithms.cpp +++ b/example/algorithms.cpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,11 @@ std::ostream& operator<<(std::ostream& os,const window& w) return os; } +template +struct overloaded:Ts...{using Ts::operator()...;}; +template +overloaded(Ts...)->overloaded; + int main() { boost::base_collection c; @@ -168,4 +174,33 @@ int main() }); std::cout<<"\n"; //] + + { +//[algorithms_9 + boost::variant_collection< + boost::mp11::mp_list + > c; +//= ... +//<- + c.insert(warrior(0)); + c.insert(std::string("play again")); + c.insert(window("player one")); +//-> + + auto print_sprite=[](const sprite& s) { s.render(std::cout); }; + auto print_string=[](const std::string& str){ std::cout<( + c.begin(),c.end(),[&](const auto& r){ + std::cout< +#include +#include "rolegame.hpp" + +template +struct overloaded:Ts...{using Ts::operator()...;}; +template +overloaded(Ts...)->overloaded; + +int main() +{ +//[basic_variant_1 +//= #include +//= ... +//= + boost::variant_collection< + boost::mp11::mp_list + > c; +//] + + { +//[basic_variant_2 + boost::variant_collection_of< + warrior,juggernaut,goblin,elf,std::string,window + > c; +//] + } + + // populate with sprites + std::mt19937 gen{92754}; // some arbitrary random seed + std::discrete_distribution<> rnd{{1,1,1,1}}; + for(int i=0;i<5;++i){ // assign each type with 1/4 probability + switch(rnd(gen)){ + case 0: c.insert(warrior{i});break; + case 1: c.insert(juggernaut{i});break; + case 2: c.insert(goblin{i});break; + case 3: c.insert(elf{i});break; + } + } + + // populate with messages + c.insert(std::string{"\"stamina: 10,000\""}); + c.insert(std::string{"\"game over\""}); + + // populate with windows + c.insert(window{"pop-up 1"}); + c.insert(window{"pop-up 2"}); + + { +//[basic_variant_3 +//= // usual utility to construct a visitor +//= template +//= struct overloaded:Ts...{using Ts::operator()...;}; +//= template +//= overloaded(Ts...)->overloaded; + + const char* comma=""; + for(const auto& r:c){ + std::cout< #include +#include +#include #include #include #include #include +#include #include #include #include @@ -143,13 +146,13 @@ struct ptr_vector:boost::ptr_vector { public: template - void insert(const T& x) + BOOST_FORCEINLINE void insert(const T& x) { this->push_back(new T{x}); } template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -182,7 +185,7 @@ template struct base_collection:boost::base_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -194,7 +197,7 @@ template struct poly_for_each_base_collection:base_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { boost::poly_collection::for_each(this->begin(),this->end(),f); } @@ -203,13 +206,13 @@ struct poly_for_each_base_collection:base_collection template struct func_vector:std::vector> { - template void insert(const T& x) + template BOOST_FORCEINLINE void insert(const T& x) { this->push_back(x); } template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -243,7 +246,7 @@ template struct func_collection:boost::function_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -255,7 +258,7 @@ template struct poly_for_each_func_collection:func_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { boost::poly_collection::for_each(this->begin(),this->end(),f); } @@ -270,7 +273,7 @@ struct any_vector:std::vector> } template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -304,7 +307,7 @@ template struct any_collection:boost::any_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) { std::for_each(this->begin(),this->end(),f); } @@ -316,7 +319,69 @@ template struct poly_for_each_any_collection:any_collection { template - void for_each(F f) + BOOST_FORCEINLINE void for_each(F f) + { + boost::poly_collection::for_each(this->begin(),this->end(),f); + } +}; + +template +struct variant_vector: + std::vector> +{ + template void insert(const T& x) + { + this->push_back(x); + } + + template + BOOST_FORCEINLINE void for_each(F f) + { + std::for_each(this->begin(),this->end(),f); + } + + void prepare_for_for_each(){} +}; + +template +struct sorted_variant_vector:variant_vector +{ + void prepare_for_for_each() + { + using value_type=typename sorted_variant_vector::value_type; + std::sort( + this->begin(),this->end(),[](const value_type& x,const value_type& y){ + return x.index() +struct shuffled_variant_vector:variant_vector +{ + void prepare_for_for_each() + { + std::shuffle(this->begin(),this->end(),std::mt19937(1)); + } +}; + +template +struct variant_collection:boost::variant_collection +{ + template + BOOST_FORCEINLINE void for_each(F f) + { + std::for_each(this->begin(),this->end(),f); + } + + void prepare_for_for_each(){} +}; + +template +struct poly_for_each_variant_collection:variant_collection +{ + template + BOOST_FORCEINLINE void for_each(F f) { boost::poly_collection::for_each(this->begin(),this->end(),f); } @@ -344,16 +409,38 @@ struct label template struct element_sequence{}; +template< + typename ElementSequence, + typename Container +> +struct container_fill_helper +{ + Container& c; + unsigned int i; + + template + void operator()(J)const + { + using T=boost::mp11::mp_at_c; + c.insert(T(i)); + } +}; + template< typename... Element, typename Container > void container_fill(unsigned int n,element_sequence,Container& c) { - auto m=n/sizeof...(Element); - for(unsigned int i=0;i!=m;++i){ - using seq=int[sizeof...(Element)]; - (void)seq{(c.insert(Element(i)),0)...}; + pause_timing(); + std::mt19937 rng{1}; + std::uniform_int_distribution<> d(0,sizeof...(Element)-1); + resume_timing(); + + for(unsigned int i=0;i!=n;++i){ + boost::mp11::mp_with_index( + d(rng), + container_fill_helper,Container>{c,i}); } } @@ -404,9 +491,7 @@ void insert_perf( for(unsigned int s=0,n=n0; (n=(unsigned int)std::round(n0*std::pow(10.0,s/1000.0)))<=n1; s+=dsav){ - unsigned int m=(unsigned int)std::round(n/sizeof...(Element)), - nn=m*sizeof...(Element); - print(nn,insert_perf(nn,elements,labels)...); + print(n,insert_perf(n,elements,labels)...); } } @@ -450,9 +535,7 @@ void for_each_perf( for(unsigned int s=0,n=n0; (n=(unsigned int)std::round(n0*std::pow(10.0,s/1000.0)))<=n1; s+=dsav){ - unsigned int m=(unsigned int)std::round(n/sizeof...(Element)), - nn=m*sizeof...(Element); - print(nn,for_each_perf(nn,elements,f,labels)...); + print(n,for_each_perf(n,elements,f,labels)...); } } @@ -485,7 +568,27 @@ struct for_each_incrementable }; //] -int main(int argc, char *argv[]) +//[perf_for_each_alternative +struct for_each_alternative +{ + for_each_alternative():res{0}{} + + template class V,typename... Ts> + void operator()(V& x){ + visit(*this,x); + } + + template + void operator()(T& x){ + ++x; + ++res; + } + + int res; +}; +//] + +int main(int argc,char *argv[]) { using test=std::pair; @@ -495,15 +598,19 @@ int main(int argc, char *argv[]) insert_function=false, for_each_function=false, insert_any=false, - for_each_any=false; - std::array tests={{ + for_each_any=false, + insert_variant=false, + for_each_variant=false; + std::array tests={{ {"all",all}, {"insert_base",insert_base}, {"for_each_base",for_each_base}, {"insert_function",insert_function}, {"for_each_function",for_each_function}, {"insert_any",insert_any}, - {"for_each_any",for_each_any} + {"for_each_any",for_each_any}, + {"insert_variant",insert_variant}, + {"for_each_variant",for_each_variant} }}; if(argc<2){ @@ -526,8 +633,7 @@ int main(int argc, char *argv[]) unsigned int n0=100,n1=10000000,dsav=50; /* sav for savart */ { - auto seq= element_sequence< - derived1,derived1,derived2,derived2,derived3>{}; + auto seq= element_sequence{}; auto f= for_each_callable{}; auto pv= label> {"ptr_vector"}; @@ -540,7 +646,7 @@ int main(int argc, char *argv[]) auto fbc= label> {"base_collection (poly::for_each)"}; auto rfbc= label< - poly_for_each_base_collection + poly_for_each_base_collection > {"base_collection (restituted poly::for_each)"}; @@ -551,8 +657,7 @@ int main(int argc, char *argv[]) { using signature=int(int); - auto seq= element_sequence< - concrete1,concrete1,concrete2,concrete2,concrete3>{}; + auto seq= element_sequence{}; auto f = for_each_callable{}; auto fv= label> {"func_vector"}; @@ -582,7 +687,7 @@ int main(int argc, char *argv[]) >; //] - auto seq= element_sequence{}; + auto seq= element_sequence{}; auto f= for_each_incrementable{}; auto av= label> {"any_vector"}; @@ -601,4 +706,30 @@ int main(int argc, char *argv[]) if(all||for_each_any)for_each_perf( n0,n1,dsav,seq,f,av,sav,shav,ac,fac,rfac); } + { +//[perf_variant_types + using type_list=boost::mp11::mp_list; +//] + + auto seq= element_sequence{}; + auto f= for_each_alternative{}; + auto vv= label> + {"variant_vector"}; + auto svv= label> + {"sorted variant_vector"}; + auto shvv= label> + {"shuffled variant_vector"}; + auto vc= label> + {"variant_collection"}; + auto fvc= label> + {"variant_collection (poly::for_each)"}; + auto rfvc= label< + poly_for_each_variant_collection< + type_list,boost::poly_collection::all_types>> + {"variant_collection (restituted poly::for_each)"}; + + if(all||insert_variant)insert_perf(n0,n1,dsav,seq,vv,vc); + if(all||for_each_variant)for_each_perf( + n0,n1,dsav,seq,f,vv,svv,shvv,vc,fvc,rfvc); + } } diff --git a/example/segmented_structure.cpp b/example/segmented_structure.cpp index ab5701c..a6cf465 100644 --- a/example/segmented_structure.cpp +++ b/example/segmented_structure.cpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -104,6 +105,22 @@ int main() //] //[segmented_structure_6 + boost::variant_collection< + boost::mp11::mp_list + > c3; +//= ... // populate c3 some entities +//= +//<- + c3.insert(warrior{0}); + c3.insert(warrior{1}); + c3.insert(std::string("game over")); + c3.insert(window{"pop-up"}); +//-> + std::cout<()<<"\n"; // same as with other collections + std::cout<(),last=c.end(); first!=last;++first){ @@ -150,10 +167,22 @@ int main() } std::cout<<"\n"; +//] + +//[segmented_structure_10 + // traverse all windows in our variant_collection + + for(auto& x:c3.segment()){ + // visitation not required: we're accessing window directly + x.display(std::cout); + } +//<- + std::cout<<"\n"; +//-> //] auto render=[&](){ -//[segmented_structure_9 +//[segmented_structure_11 const char* comma=""; for(auto seg:c.segment_traversal()){ for(sprite& s:seg){ @@ -167,12 +196,12 @@ int main() }; render(); -//[segmented_structure_10 +//[segmented_structure_12 c.reserve(100); // no reallocation till we exceed 100 goblins std::cout<()<<"\n"; // prints 100 //] -//[segmented_structure_11 +//[segmented_structure_13 c.reserve(1000); // reserve(1000) for each segment std::cout<()<<", " <()<<", " diff --git a/include/boost/poly_collection/algorithm.hpp b/include/boost/poly_collection/algorithm.hpp index 67c4fc3..29b62dd 100644 --- a/include/boost/poly_collection/algorithm.hpp +++ b/include/boost/poly_collection/algorithm.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2020 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -14,7 +14,9 @@ #endif #include +#include #include +#include #include #include #include @@ -37,6 +39,11 @@ * being a particular example). */ +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + namespace boost{ namespace poly_collection{ @@ -56,9 +63,12 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > +BOOST_FORCEINLINE bool all_of(const Iterator& first,const Iterator& last,Predicate pred) { - auto alg=restitute_range(std_all_of{},pred); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range(std_all_of{},pred); for(auto i:detail::segment_split(first,last))if(!alg(i))return false; return true; } @@ -69,9 +79,12 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -bool any_of(const Iterator& first,const Iterator& last,Predicate pred) +BOOST_FORCEINLINE bool any_of( + const Iterator& first,const Iterator& last,Predicate pred) { - auto alg=restitute_range(std_any_of{},pred); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range(std_any_of{},pred); for(auto i:detail::segment_split(first,last))if(alg(i))return true; return false; } @@ -82,9 +95,12 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -bool none_of(const Iterator& first,const Iterator& last,Predicate pred) +BOOST_FORCEINLINE bool none_of( + const Iterator& first,const Iterator& last,Predicate pred) { - auto alg=restitute_range(std_none_of{},pred); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range(std_none_of{},pred); for(auto i:detail::segment_split(first,last))if(!alg(i))return false; return true; } @@ -99,13 +115,16 @@ struct for_each_alg } }; -template< - typename... Ts,typename Iterator,typename Function, +template =nullptr > -Function for_each(const Iterator& first,const Iterator& last,Function f) +BOOST_FORCEINLINE Function for_each( + const Iterator& first,const Iterator& last,Function f) { - for_each_segment(first,last,restitute_range(for_each_alg{},f)); + using model_type=typename iterator_traits::model_type; + + for_each_segment( + first,last,restitute_range(for_each_alg{},f)); return f; } @@ -126,19 +145,20 @@ template< typename... Ts,typename Iterator,typename Size,typename Function, enable_if_poly_collection_iterator =nullptr > -Iterator for_each_n(const Iterator& first,Size n,Function f) +BOOST_FORCEINLINE Iterator for_each_n(const Iterator& first,Size n,Function f) { using traits=iterator_traits; + using model_type=typename traits::model_type; using local_base_iterator=typename traits::local_base_iterator; if(n<=0)return first; - auto alg=restitute_iterator( + auto alg=restitute_iterator( cast_return(for_each_n_alg{})); auto lbit=traits::local_base_iterator_from(first); auto sit=traits::base_segment_info_iterator_from(first); for(;;){ - auto m=sit->end()-lbit; + Size m=static_cast(sit->end()-lbit); if(n<=m){ auto it=alg(sit->type_info(),lbit,n,f); return traits::iterator_from( @@ -158,13 +178,14 @@ template< typename Iterator,typename... Args, enable_if_poly_collection_iterator =nullptr > -Iterator generic_find( +BOOST_FORCEINLINE Iterator generic_find( const Iterator& first,const Iterator& last,Args&&... args) { using traits=iterator_traits; + using model_type=typename traits::model_type; using local_base_iterator=typename traits::local_base_iterator; - auto alg=restitute_range( + auto alg=restitute_range( cast_return(Algorithm{}), std::forward(args)...); for(auto i:detail::segment_split(first,last)){ @@ -182,7 +203,8 @@ template< typename... Ts,typename Iterator,typename T, enable_if_poly_collection_iterator =nullptr > -Iterator find(const Iterator& first,const Iterator& last,const T& x) +BOOST_FORCEINLINE Iterator find( + const Iterator& first,const Iterator& last,const T& x) { return generic_find(first,last,x); } @@ -193,7 +215,8 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -Iterator find_if(const Iterator& first,const Iterator& last,Predicate pred) +BOOST_FORCEINLINE Iterator find_if( + const Iterator& first,const Iterator& last,Predicate pred) { return generic_find(first,last,pred); } @@ -204,7 +227,8 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -Iterator find_if_not(const Iterator& first,const Iterator& last,Predicate pred) +BOOST_FORCEINLINE Iterator find_if_not( + const Iterator& first,const Iterator& last,Predicate pred) { return generic_find(first,last,pred); } @@ -217,7 +241,7 @@ template< typename... Ts,typename Iterator,typename ForwardIterator, enable_if_poly_collection_iterator =nullptr > -Iterator find_first_of( +BOOST_FORCEINLINE Iterator find_first_of( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2) { @@ -229,7 +253,7 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -Iterator find_first_of( +BOOST_FORCEINLINE Iterator find_first_of( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2,BinaryPredicate pred) { @@ -240,23 +264,27 @@ template struct adjacent_find_alg { template< - typename LocalIterator,typename BinaryPredicate,typename LocalBaseIterator + typename LocalIterator,typename BinaryPredicate, + typename TypeIndex,typename LocalBaseIterator > LocalBaseIterator operator()( LocalIterator first,LocalIterator last,BinaryPredicate pred, - bool& carry,const std::type_info* prev_info, /* note the &s */ + bool& carry,TypeIndex& prev_info, /* note the &s */ LocalBaseIterator& prev)const { + using traits=iterator_traits; + using model_type=typename traits::model_type; + if(first==last)return LocalBaseIterator{last}; if(carry){ - auto p=restitute_iterator(deref_to(pred)); - if(p(*prev_info,prev,first))return prev; + auto p=restitute_iterator(deref_to(pred)); + if(p(prev_info,prev,first))return prev; } auto res=std::adjacent_find(first,last,pred); if(res==last){ carry=true; - prev_info=&typeid( - typename std::iterator_traits::value_type); + prev_info=traits::template index< + typename std::iterator_traits::value_type>(); prev=LocalBaseIterator{last-1}; } else carry=false; @@ -268,14 +296,15 @@ template< typename... Ts,typename Iterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -Iterator adjacent_find( +BOOST_FORCEINLINE Iterator adjacent_find( const Iterator& first,const Iterator& last,BinaryPredicate pred) { using traits=iterator_traits; using local_base_iterator=typename traits::local_base_iterator; bool carry=false; - const std::type_info* prev_info{&typeid(void)}; + auto prev_info= + make_copyable(traits::template index()); local_base_iterator prev; return generic_find,Ts...>( first,last,pred,carry,prev_info,prev); @@ -285,7 +314,8 @@ template< typename... Ts,typename Iterator, enable_if_poly_collection_iterator =nullptr > -Iterator adjacent_find(const Iterator& first,const Iterator& last) +BOOST_FORCEINLINE Iterator adjacent_find( + const Iterator& first,const Iterator& last) { return algorithm::adjacent_find(first,last,transparent_equal_to{}); } @@ -295,10 +325,13 @@ template< typename Iterator,typename... Args, enable_if_poly_collection_iterator =nullptr > -std::ptrdiff_t generic_count( +BOOST_FORCEINLINE std::ptrdiff_t generic_count( const Iterator& first,const Iterator& last,Args&&... args) { - auto alg=restitute_range(Algorithm{},std::forward(args)...); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range( + Algorithm{},std::forward(args)...); std::ptrdiff_t res=0; for(auto i:detail::segment_split(first,last))res+=alg(i); return res; @@ -310,7 +343,8 @@ template< typename... Ts,typename Iterator,typename T, enable_if_poly_collection_iterator =nullptr > -std::ptrdiff_t count(const Iterator& first,const Iterator& last,const T& x) +BOOST_FORCEINLINE std::ptrdiff_t count( + const Iterator& first,const Iterator& last,const T& x) { return generic_count(first,last,x); } @@ -321,7 +355,7 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -std::ptrdiff_t count_if( +BOOST_FORCEINLINE std::ptrdiff_t count_if( const Iterator& first,const Iterator& last,Predicate pred) { return generic_count(first,last,pred); @@ -366,7 +400,7 @@ template< typename InputIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -std::pair mismatch( +BOOST_FORCEINLINE std::pair mismatch( const Iterator& first1,const Iterator& last1, InputIterator first2,BinaryPredicate pred) { @@ -378,7 +412,7 @@ template< typename... Ts,typename Iterator,typename InputIterator, enable_if_poly_collection_iterator =nullptr > -std::pair mismatch( +BOOST_FORCEINLINE std::pair mismatch( const Iterator& first1,const Iterator& last1,InputIterator first2) { return algorithm::mismatch( @@ -390,7 +424,7 @@ template< typename InputIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -std::pair mismatch( +BOOST_FORCEINLINE std::pair mismatch( const Iterator& first1,const Iterator& last1, InputIterator first2,InputIterator last2,BinaryPredicate pred) { @@ -402,7 +436,7 @@ template< typename... Ts,typename Iterator,typename InputIterator, enable_if_poly_collection_iterator =nullptr > -std::pair mismatch( +BOOST_FORCEINLINE std::pair mismatch( const Iterator& first1,const Iterator& last1, InputIterator first2,InputIterator last2) { @@ -447,11 +481,13 @@ template< typename InputIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -bool equal( +BOOST_FORCEINLINE bool equal( const Iterator& first1,const Iterator& last1, InputIterator first2,BinaryPredicate pred) { - auto alg=restitute_range(equal_alg{},first2,pred); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range(equal_alg{},first2,pred); for(auto i:detail::segment_split(first1,last1))if(!alg(i))return false; return true; } @@ -460,7 +496,7 @@ template< typename... Ts,typename Iterator,typename InputIterator, enable_if_poly_collection_iterator =nullptr > -bool equal( +BOOST_FORCEINLINE bool equal( const Iterator& first1,const Iterator& last1,InputIterator first2) { return algorithm::equal(first1,last1,first2,transparent_equal_to{}); @@ -471,11 +507,13 @@ template< typename InputIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -bool equal( +BOOST_FORCEINLINE bool equal( const Iterator& first1,const Iterator& last1, InputIterator first2,InputIterator last2,BinaryPredicate pred) { - auto alg=restitute_range(equal_alg{},first2,last2,pred); + using model_type=typename iterator_traits::model_type; + + auto alg=restitute_range(equal_alg{},first2,last2,pred); for(auto i:detail::segment_split(first1,last1))if(!alg(i))return false; return first2==last2; } @@ -484,7 +522,7 @@ template< typename... Ts,typename Iterator,typename InputIterator, enable_if_poly_collection_iterator =nullptr > -bool equal( +BOOST_FORCEINLINE bool equal( const Iterator& first1,const Iterator& last1, InputIterator first2,InputIterator last2) { @@ -496,7 +534,8 @@ template< typename Iterator, enable_if_poly_collection_iterator =nullptr > -std::ptrdiff_t fast_distance(const Iterator& first,const Iterator& last) +BOOST_FORCEINLINE std::ptrdiff_t fast_distance( + const Iterator& first,const Iterator& last) { using traits=iterator_traits; @@ -526,18 +565,19 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -bool is_permutation_suffix( +BOOST_FORCEINLINE bool is_permutation_suffix( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2,BinaryPredicate pred) { using traits=iterator_traits; + using model_type=typename traits::model_type; auto send=traits::end_base_segment_info_iterator_from(last1); for(auto i:detail::segment_split(first1,last1)){ for(auto lbscan=i.begin();lbscan!=i.end();++lbscan){ auto& info=i.type_info(); auto p=head_closure( - restitute_iterator(deref_1st_to(pred)),info,lbscan); + restitute_iterator(deref_1st_to(pred)),info,lbscan); auto scan=traits::iterator_from(lbscan,send); if(algorithm::find_if(first1,scan,p)!=scan)continue; std::ptrdiff_t matches=std::count_if(first2,last2,p); @@ -553,7 +593,7 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -bool is_permutation( +BOOST_FORCEINLINE bool is_permutation( Iterator first1,Iterator last1,ForwardIterator first2,BinaryPredicate pred) { std::tie(first1,first2)=algorithm::mismatch(first1,last1,first2,pred); @@ -565,7 +605,7 @@ template< typename... Ts,typename Iterator,typename ForwardIterator, enable_if_poly_collection_iterator =nullptr > -bool is_permutation( +BOOST_FORCEINLINE bool is_permutation( const Iterator& first1,const Iterator& last1,ForwardIterator first2) { return algorithm::is_permutation( @@ -577,7 +617,7 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -bool is_permutation( +BOOST_FORCEINLINE bool is_permutation( Iterator first1,Iterator last1, ForwardIterator first2,ForwardIterator last2,BinaryPredicate pred) { @@ -592,7 +632,7 @@ template< typename... Ts,typename Iterator,typename ForwardIterator, enable_if_poly_collection_iterator =nullptr > -bool is_permutation( +BOOST_FORCEINLINE bool is_permutation( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2) { @@ -605,7 +645,7 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -Iterator search( +BOOST_FORCEINLINE Iterator search( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2,BinaryPredicate pred) { @@ -626,7 +666,7 @@ template< typename... Ts,typename Iterator,typename ForwardIterator, enable_if_poly_collection_iterator =nullptr > -Iterator search( +BOOST_FORCEINLINE Iterator search( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2) { @@ -639,7 +679,7 @@ template< typename ForwardIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -Iterator find_end( +BOOST_FORCEINLINE Iterator find_end( Iterator first1,Iterator last1, ForwardIterator first2,ForwardIterator last2,BinaryPredicate pred) { @@ -659,7 +699,7 @@ template< typename... Ts,typename Iterator,typename ForwardIterator, enable_if_poly_collection_iterator =nullptr > -Iterator find_end( +BOOST_FORCEINLINE Iterator find_end( const Iterator& first1,const Iterator& last1, ForwardIterator first2,ForwardIterator last2) { @@ -695,18 +735,19 @@ template< typename Size,typename T,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -Iterator search_n( +BOOST_FORCEINLINE Iterator search_n( const Iterator& first,const Iterator& last, Size count,const T& x,BinaryPredicate pred) { using traits=iterator_traits; + using model_type=typename traits::model_type; using local_base_iterator=typename traits::local_base_iterator; if(count<=0)return first; bool carry=false; auto remain=count; - auto alg=restitute_range( + auto alg=restitute_range( cast_return(search_n_alg{}), count,carry,remain,x,pred); local_base_iterator prev; @@ -728,7 +769,7 @@ template< typename Size,typename T, enable_if_poly_collection_iterator =nullptr > -Iterator search_n( +BOOST_FORCEINLINE Iterator search_n( const Iterator& first,const Iterator& last,Size count,const T& x) { return algorithm::search_n( @@ -740,11 +781,13 @@ template< typename Iterator,typename OutputIterator,typename... Args, enable_if_poly_collection_iterator =nullptr > -OutputIterator generic_copy( +BOOST_FORCEINLINE OutputIterator generic_copy( const Iterator& first,const Iterator& last,OutputIterator res,Args&&... args) { + using model_type=typename iterator_traits::model_type; + for(auto i:detail::segment_split(first,last)){ - auto alg=restitute_range( + auto alg=restitute_range( Algorithm{},res,std::forward(args)...); res=alg(i); } @@ -757,7 +800,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator, enable_if_poly_collection_iterator =nullptr > -OutputIterator copy( +BOOST_FORCEINLINE OutputIterator copy( const Iterator& first,const Iterator& last,OutputIterator res) { return generic_copy(first,last,res); @@ -769,17 +812,19 @@ template< typename... Ts,typename Iterator,typename Size,typename OutputIterator, enable_if_poly_collection_iterator =nullptr > -OutputIterator copy_n(const Iterator& first,Size count,OutputIterator res) +BOOST_FORCEINLINE OutputIterator copy_n( + const Iterator& first,Size count,OutputIterator res) { using traits=iterator_traits; + using model_type=typename traits::model_type; if(count<=0)return res; auto lbit=traits::local_base_iterator_from(first); auto sit=traits::base_segment_info_iterator_from(first); for(;;){ - auto n=(std::min)(count,sit->end()-lbit); - auto alg=restitute_iterator(std_copy_n{},n,res); + auto n=(std::min)(count,static_cast(sit->end()-lbit)); + auto alg=restitute_iterator(std_copy_n{},n,res); res=alg(sit->type_info(),lbit); if((count-=n)==0)break; ++sit; @@ -794,7 +839,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -OutputIterator copy_if( +BOOST_FORCEINLINE OutputIterator copy_if( const Iterator& first,const Iterator& last,OutputIterator res,Predicate pred) { return generic_copy(first,last,res,pred); @@ -806,7 +851,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator, enable_if_poly_collection_iterator =nullptr > -OutputIterator move( +BOOST_FORCEINLINE OutputIterator move( const Iterator& first,const Iterator& last,OutputIterator res) { return generic_copy(first,last,res); @@ -819,7 +864,7 @@ template< typename OutputIterator,typename UnaryOperation, enable_if_poly_collection_iterator =nullptr > -OutputIterator transform( +BOOST_FORCEINLINE OutputIterator transform( const Iterator& first,const Iterator& last, OutputIterator res,UnaryOperation op) { @@ -847,7 +892,7 @@ template< typename OutputIterator,typename BinaryOperation, enable_if_poly_collection_iterator =nullptr > -OutputIterator transform( +BOOST_FORCEINLINE OutputIterator transform( const Iterator& first1,const Iterator& last1,InputIterator first2, OutputIterator res,BinaryOperation op) { @@ -878,7 +923,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator,typename T, enable_if_poly_collection_iterator =nullptr > -OutputIterator replace_copy( +BOOST_FORCEINLINE OutputIterator replace_copy( const Iterator& first,const Iterator& last,OutputIterator res, const T& old_x,const T& new_x) { @@ -913,7 +958,7 @@ template< typename Predicate,typename T, enable_if_poly_collection_iterator =nullptr > -OutputIterator replace_copy_if( +BOOST_FORCEINLINE OutputIterator replace_copy_if( const Iterator& first,const Iterator& last,OutputIterator res, Predicate pred,const T& new_x) { @@ -926,7 +971,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator,typename T, enable_if_poly_collection_iterator =nullptr > -OutputIterator remove_copy( +BOOST_FORCEINLINE OutputIterator remove_copy( const Iterator& first,const Iterator& last,OutputIterator res,const T& x) { return generic_copy(first,last,res,x); @@ -939,7 +984,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -OutputIterator remove_copy_if( +BOOST_FORCEINLINE OutputIterator remove_copy_if( const Iterator& first,const Iterator& last,OutputIterator res,Predicate pred) { return generic_copy(first,last,res,pred); @@ -950,23 +995,26 @@ struct unique_copy_alg { template< typename LocalIterator,typename OutputIterator, - typename BinaryPredicate,typename LocalBaseIterator + typename BinaryPredicate,typename TypeIndex,typename LocalBaseIterator > OutputIterator operator()( LocalIterator first,LocalIterator last, OutputIterator res, BinaryPredicate pred, - bool& carry,const std::type_info* prev_info, /* note the &s */ + bool& carry,TypeIndex& prev_info, /* note the &s */ LocalBaseIterator& prev)const { + using traits=iterator_traits; + using model_type=typename traits::model_type; + if(carry){ - auto p=restitute_iterator(deref_to(pred)); - for(;first!=last;++first)if(!p(*prev_info,prev,first))break; + auto p=restitute_iterator(deref_to(pred)); + for(;first!=last;++first)if(!p(prev_info,prev,first))break; } if(first==last)return res; res=std::unique_copy(first,last,res,pred); carry=true; - prev_info=&typeid( - typename std::iterator_traits::value_type); + prev_info=traits::template index< + typename std::iterator_traits::value_type>(); prev=LocalBaseIterator{last-1}; return res; } @@ -977,7 +1025,7 @@ template< typename OutputIterator,typename BinaryPredicate, enable_if_poly_collection_iterator =nullptr > -OutputIterator unique_copy( +BOOST_FORCEINLINE OutputIterator unique_copy( const Iterator& first,const Iterator& last, OutputIterator res,BinaryPredicate pred) { @@ -985,7 +1033,8 @@ OutputIterator unique_copy( using local_base_iterator=typename traits::local_base_iterator; bool carry=false; - const std::type_info* prev_info{&typeid(void)}; + auto prev_info= + make_copyable(traits::template index()); local_base_iterator prev; return generic_copy,Ts...>( first,last,res,pred,carry,prev_info,prev); @@ -995,7 +1044,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator, enable_if_poly_collection_iterator =nullptr > -OutputIterator unique_copy( +BOOST_FORCEINLINE OutputIterator unique_copy( const Iterator& first,const Iterator& last,OutputIterator res) { return algorithm::unique_copy(first,last,res,transparent_equal_to{}); @@ -1005,7 +1054,7 @@ template< typename... Ts,typename Iterator,typename OutputIterator, enable_if_poly_collection_iterator =nullptr > -OutputIterator rotate_copy( +BOOST_FORCEINLINE OutputIterator rotate_copy( const Iterator& first,const Iterator& middle,const Iterator& last, OutputIterator res) { @@ -1040,15 +1089,17 @@ template< typename Distance,typename UniformRandomBitGenerator, enable_if_poly_collection_iterator =nullptr > -OutputIterator sample( +BOOST_FORCEINLINE OutputIterator sample( const Iterator& first,const Iterator& last, OutputIterator res,Distance n,UniformRandomBitGenerator&& g) { - Distance m=algorithm::fast_distance(first,last); + using model_type=typename iterator_traits::model_type; + + Distance m=static_cast(algorithm::fast_distance(first,last)); n=(std::min)(n,m); for(auto i:detail::segment_split(first,last)){ - auto alg=restitute_range(sample_alg{},res,n,m,g); + auto alg=restitute_range(sample_alg{},res,n,m,g); res=alg(i); if(n==0)return res; } @@ -1059,7 +1110,8 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -bool is_partitioned(const Iterator& first,const Iterator& last,Predicate pred) +BOOST_FORCEINLINE bool is_partitioned( + const Iterator& first,const Iterator& last,Predicate pred) { auto it=algorithm::find_if_not(first,last,pred); if(it==last)return true; @@ -1074,12 +1126,15 @@ template< typename OutputIterator1,typename OutputIterator2,typename Predicate, enable_if_poly_collection_iterator =nullptr > -std::pair partition_copy( +BOOST_FORCEINLINE std::pair partition_copy( const Iterator& first,const Iterator& last, OutputIterator1 rest,OutputIterator2 resf,Predicate pred) { + using model_type=typename iterator_traits::model_type; + for(auto i:detail::segment_split(first,last)){ - auto alg=restitute_range(std_partition_copy{},rest,resf,pred); + auto alg=restitute_range( + std_partition_copy{},rest,resf,pred); std::tie(rest,resf)=alg(i); } return {rest,resf}; @@ -1094,7 +1149,9 @@ struct partition_point_pred bool operator()(const Iterator& it)const { using traits=iterator_traits; - auto p=restitute_iterator(deref_to(pred)); + using model_type=typename traits::model_type; + + auto p=restitute_iterator(deref_to(pred)); return p( traits::base_segment_info_iterator_from(it)->type_info(), traits::local_base_iterator_from(it)); @@ -1107,7 +1164,7 @@ template< typename... Ts,typename Iterator,typename Predicate, enable_if_poly_collection_iterator =nullptr > -Iterator partition_point( +BOOST_FORCEINLINE Iterator partition_point( const Iterator& first,const Iterator& last,Predicate pred) { auto_iterator afirst{first},alast{last}; @@ -1183,4 +1240,8 @@ using detail::algorithm::partition_point; } /* namespace boost */ +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + #endif diff --git a/include/boost/poly_collection/detail/allocator_adaptor.hpp b/include/boost/poly_collection/detail/allocator_adaptor.hpp index 9652e26..47a43eb 100644 --- a/include/boost/poly_collection/detail/allocator_adaptor.hpp +++ b/include/boost/poly_collection/detail/allocator_adaptor.hpp @@ -1,4 +1,4 @@ -/* Copyright 2018 Joaquin M Lopez Munoz. +/* Copyright 2018-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -36,13 +36,13 @@ namespace detail{ * prevents intermediate entities from calling Allocator::[construct|destroy]. * allocator_adaptor does this by taking advantage of the fact that * elements are ultimately held within a value_holder: - * - construct(value_holder*,...) uses placement new construction and + * - construct(value_holder*,...) uses placement new construction and * passes the wrapped Allocator object for value_holder to use for * its inner construction of T. * - For the rest of types, construct uses placement new construction and * passes down the adaptor object itself as an argument following an * approach analogous to that of std::scoped_allocator_adaptor. - * - destroy(value_holder) resorts to Allocator::destroy to destroy the + * - destroy(value_holder) resorts to Allocator::destroy to destroy the * contained T element. * - For the rest of types, destroy(T) calls ~T directly. * @@ -53,7 +53,7 @@ namespace detail{ template class value_holder_base; -template +template class value_holder; template @@ -144,10 +144,11 @@ struct allocator_adaptor:Allocator p,std::forward(args)...); } - template - void construct(value_holder* p,Args&&... args) + template + void construct(value_holder* p,Args&&... args) { - ::new ((void*)p) value_holder(allocator(),std::forward(args)...); + ::new ((void*)p) value_holder( + allocator(),std::forward(args)...); } template @@ -206,8 +207,8 @@ struct allocator_adaptor:Allocator p->~T(); } - template - void destroy(value_holder* p) + template + void destroy(value_holder* p) { traits::destroy( allocator(), diff --git a/include/boost/poly_collection/detail/any_model.hpp b/include/boost/poly_collection/detail/any_model.hpp index e4a161a..2e8799a 100644 --- a/include/boost/poly_collection/detail/any_model.hpp +++ b/include/boost/poly_collection/detail/any_model.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2020 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -100,20 +100,25 @@ struct any_model type_erasure::_self& >; + using type_index=std::type_info; + template using is_implementation=std::true_type; /* can't compile-time check concept * compliance */ template using is_terminal=any_model_is_terminal; + template + static const std::type_info& index(){return typeid(T);} + template - static const std::type_info& subtypeid(const T&){return typeid(T);} + static const std::type_info& subindex(const T&){return typeid(T);} template< typename Concept2,typename T, any_model_enable_if_has_typeid_ =nullptr > - static const std::type_info& subtypeid( + static const std::type_info& subindex( const type_erasure::any& a) { return type_erasure::typeid_of(a); diff --git a/include/boost/poly_collection/detail/base_model.hpp b/include/boost/poly_collection/detail/base_model.hpp index 62c209f..c79ecd6 100644 --- a/include/boost/poly_collection/detail/base_model.hpp +++ b/include/boost/poly_collection/detail/base_model.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2018 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -34,6 +34,7 @@ template struct base_model { using value_type=Base; + using type_index=std::type_info; template using is_implementation=std::is_base_of; template @@ -48,11 +49,14 @@ private: typename std::enable_if::value>::type*; public: + template + static const std::type_info& index(){return typeid(T);} + template =nullptr> - static const std::type_info& subtypeid(const T& x){return typeid(x);} + static const std::type_info& subindex(const T& x){return typeid(x);} template =nullptr> - static const std::type_info& subtypeid(const T&){return typeid(T);} + static const std::type_info& subindex(const T&){return typeid(T);} template =nullptr> static void* subaddress(T& x) diff --git a/include/boost/poly_collection/detail/begin_no_sanitize.hpp b/include/boost/poly_collection/detail/begin_no_sanitize.hpp new file mode 100644 index 0000000..b2c3a70 --- /dev/null +++ b/include/boost/poly_collection/detail/begin_no_sanitize.hpp @@ -0,0 +1,31 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#if BOOST_WORKAROUND(BOOST_GCC_VERSION,>=40900)||\ + BOOST_WORKAROUND(BOOST_CLANG,>=1)&&\ + (__clang_major__>3 || __clang_major__==3 && __clang_minor__ >= 8) +/* https://github.com/boostorg/poly_collection/issues/15 */ + +#define BOOST_POLY_COLLECTION_INSIDE_NO_SANITIZE +#define BOOST_POLY_COLLECTION_NO_SANITIZE \ +__attribute__((no_sanitize("undefined"))) + +/* UBSan seems not to be supported in some environments */ +#if defined(BOOST_GCC_VERSION) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wattributes" +#elif defined(BOOST_CLANG) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wattributes" +#endif + +#else + +#define BOOST_POLY_COLLECTION_NO_SANITIZE + +#endif diff --git a/include/boost/poly_collection/detail/copyable.hpp b/include/boost/poly_collection/detail/copyable.hpp new file mode 100644 index 0000000..baa4186 --- /dev/null +++ b/include/boost/poly_collection/detail/copyable.hpp @@ -0,0 +1,59 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_COPYABLE_HPP +#define BOOST_POLY_COLLECTION_DETAIL_COPYABLE_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +/* wraps T if it is not copyable */ + +template +struct copyable_impl{using type=T;}; + +template +struct reference_wrapper +{ + reference_wrapper(T& x):p{&x}{} + + operator T&()const noexcept{return *p;} + + T* p; +}; + +template +struct copyable_impl< + T, + typename std::enable_if::value>::type +> +{using type=reference_wrapper;}; + +template +using copyable=typename copyable_impl::type; + +template +copyable make_copyable(const T& x){return x;} + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/end_no_sanitize.hpp b/include/boost/poly_collection/detail/end_no_sanitize.hpp new file mode 100644 index 0000000..10d0770 --- /dev/null +++ b/include/boost/poly_collection/detail/end_no_sanitize.hpp @@ -0,0 +1,18 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#if defined(BOOST_POLY_COLLECTION_INSIDE_NO_SANITIZE) +#if defined(BOOST_GCC_VERSION) +#pragma GCC diagnostic pop +#elif defined(BOOST_CLANG) +#pragma clang diagnostic pop +#endif + +#undef BOOST_POLY_COLLECTION_NO_SANITIZE +#undef BOOST_POLY_COLLECTION_INSIDE_NO_SANITIZE +#endif diff --git a/include/boost/poly_collection/detail/fixed_variant.hpp b/include/boost/poly_collection/detail/fixed_variant.hpp new file mode 100644 index 0000000..2068af2 --- /dev/null +++ b/include/boost/poly_collection/detail/fixed_variant.hpp @@ -0,0 +1,602 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP +#define BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace fixed_variant_impl{ + +/* fixed_variant behaves as a variant without the possibility to change the + * initial alternative. The referenced value does not belong in fixed_variant + * but assumed to be right before it, which layout is implemented by + * fixed_variant_closure>. This approach allows us to pack + * same-alternative fixed_variants optimally, without reserving space for + * the largest alternative type. + */ + +template +class fixed_variant +{ + static_assert( + mp11::mp_is_set::value, + "all types in the variant must be distinct"); + +public: + template< + typename T, + std::size_t I=mp11::mp_find::value, + typename std::enable_if< + (I::value)>::type* =nullptr + > + explicit fixed_variant(const T&):index_{I}{} + + std::size_t index()const noexcept{return index_;} + bool valueless_by_exception()const noexcept{return false;} + +private: + std::size_t index_; +}; + +template +struct fixed_variant_store +{ + template + fixed_variant_store(Args&&... args):value{std::forward(args)...}{} + + T value; +}; + +template +class fixed_variant_closure:public fixed_variant_store,public Base +{ +public: + template< + typename... Args, + typename std::enable_if< + std::is_constructible::value + >::type* =nullptr + > + fixed_variant_closure(Args&&... args) + noexcept(std::is_nothrow_constructible::value): + fixed_variant_store{std::forward(args)...}, + Base{this->value} + {} + + fixed_variant_closure(const fixed_variant_closure&)=default; + fixed_variant_closure(fixed_variant_closure&&)=default; + fixed_variant_closure& operator=(fixed_variant_closure&&)=default; + + template< + typename Q=T, + typename std::enable_if< + detail::is_equality_comparable::value,bool>::type* =nullptr + > + bool operator==(const fixed_variant_closure& x)const + noexcept(detail::is_nothrow_equality_comparable::value) + { + return this->value==x.value; + } +}; + +struct bad_variant_access:std::exception +{ + bad_variant_access()noexcept{} + const char* what()const noexcept{return "bad variant access";} +}; + +template struct variant_size; +template +struct variant_size>: + std::integral_constant{}; +template struct variant_size:variant_size{}; + +#ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES +template +constexpr std::size_t variant_size_v=variant_size::value; +#endif + +template struct variant_alternative; + +template struct is_fixed_variant:std::false_type{}; +template +struct is_fixed_variant>:std::true_type{}; + +template +struct transfer_cref{using type=Q;}; +template +struct transfer_cref{using type=const Q;}; +template +struct transfer_cref +{using type=typename transfer_cref::type&;}; +template +struct transfer_cref +{using type=typename transfer_cref::type&&;}; + +template +struct variant_alternative< + I,V, + typename std::enable_if< + is_fixed_variant::type>::value + >::type +> +{ + using type=typename transfer_cref< + V,mp11::mp_at_c::type,I>>::type; +}; + +template +using variant_alternative_t=typename variant_alternative::type; + +template +bool holds_alternative(const fixed_variant& x)noexcept +{ + static_assert( + mp11::mp_contains,T>::value, + "type must be one of the variant alternatives"); + return x.index()==mp11::mp_find,T>::value; +} + +template +const T& unsafe_get(const fixed_variant& x) +{ + return static_cast>&> + (x).value; +} + +template +T& unsafe_get(fixed_variant& x) +{ + return const_cast( + unsafe_get(const_cast&>(x))); +} + +template +const T&& unsafe_get(const fixed_variant&& x) +{ + return std::move(unsafe_get(x)); +} + +template +T&& unsafe_get(fixed_variant&& x) +{ + return std::move(unsafe_get(x)); +} + +template +variant_alternative_t unsafe_get(V&& x) +{ + using raw_variant=typename std::decay::type; + + return unsafe_get>(std::forward(x)); +} + +template +variant_alternative_t get(V&& x) +{ + using raw_variant=typename std::decay::type; + static_assert( + I::value, + "index must be less than the number of alternatives"); + + if(x.index()!=I)throw bad_variant_access{}; + else return unsafe_get(std::forward(x)); +} + +template +auto get(V&& x)->decltype(unsafe_get(std::forward(x))) +{ + using raw_variant=typename std::decay::type; + static_assert( + mp11::mp_contains::value, + "type must be one of the variant alternatives"); + + if(!holds_alternative(x))throw bad_variant_access{}; + else return unsafe_get(std::forward(x)); +} + +template +variant_alternative_t>* +get_if(fixed_variant* px)noexcept +{ + if(!px||px->index()!=I)return nullptr; + else return std::addressof(unsafe_get(*px)); +} + +template +const variant_alternative_t>* +get_if(const fixed_variant* px)noexcept +{ + return get_if(const_cast*>(px)); +} + +template +T* get_if(fixed_variant* px)noexcept +{ + if(!px||!holds_alternative(*px))return nullptr; + else return std::addressof(unsafe_get(*px)); +} + +template +const T* get_if(const fixed_variant* px)noexcept +{ + return get_if(const_cast*>(px)); +} + +struct deduced; + +template +struct return_type_impl +{ + using type=R; +}; + +template +struct return_type_impl +{ + using type=decltype(std::declval()(get<0>(std::declval())...)); +}; + +template +using return_type=typename return_type_impl::type; + +template +struct visit_helper; + +template +struct visit_helper +{ + F&& f; + + R operator()(){return std::forward(f)();} +}; + +template +struct visit_helper +{ + F&& f; + + void operator()(){(void)std::forward(f)();} +}; + +template< + typename R=deduced,typename F, + typename ReturnType=return_type +> +ReturnType visit(F&& f) +{ + return visit_helper{std::forward(f)}(); +} + +template +struct visit_helper +{ + F&& f; + V&& x; + + template + R operator()(I) + { + return std::forward(f)(unsafe_get(std::forward(x))); + } +}; + +template +struct visit_helper +{ + F&& f; + V&& x; + + template + void operator()(I) + { + (void)std::forward(f)(unsafe_get(std::forward(x))); + } +}; + +template< + typename R=deduced,typename F,typename V, + typename ReturnType=return_type +> +ReturnType visit(F&& f,V&& x) +{ + using raw_variant=typename std::decay::type; + + return mp11::mp_with_index::value>( + x.index(), + visit_helper{std::forward(f),std::forward(x)}); +} + +template +struct bound_f +{ + F&& f; + V&& x; + + template + R operator()(Args&&... xs) + { + return std::forward(f)( + unsafe_get(std::forward(x)),std::forward(xs)...); + } +}; + +template +struct bound_f +{ + F&& f; + V&& x; + + template + void operator()(Args&&... xs) + { + (void)std::forward(f)( + unsafe_get(std::forward(x)),std::forward(xs)...); + } +}; + +template +struct bound_visit; + +template +struct visit_helper +{ + F&& f; + V&& x; + std::tuple xs; + + template + R operator()(I) + { + return mp11::tuple_apply( + bound_visit>{{std::forward(f),std::forward(x)}}, + std::move(xs)); + } +}; + +template< + typename R=deduced,typename F,typename V1,typename V2,typename... Vs, + typename ReturnType=return_type +> +ReturnType visit(F&& f,V1&& x1,V2&& x2,Vs&&... xs) +{ + using raw_variant=typename std::decay::type; + + return mp11::mp_with_index::value>( + x1.index(), + visit_helper{ + std::forward(f),std::forward(x1), + std::forward_as_tuple(std::forward(x2),std::forward(xs)...)}); +} + +template +struct bound_visit +{ + F&& f; + + template + R operator()(Vs&&... xs) + { + return visit(std::forward(f),std::forward(xs)...); + } +}; + +template +struct return_type_by_index_impl +{ + using type=R; +}; + +template +struct return_type_by_index_impl +{ + using type=decltype(std::declval()(get<0>(std::declval()))); +}; + +template +using return_type_by_index=typename return_type_by_index_impl::type; + +template +struct visit_by_index_helper +{ + V&& x; + std::tuple fs; + + template + R operator()(I) + { + return std::get(std::move(fs))( + unsafe_get(std::forward(x))); + } +}; + +template +struct visit_by_index_helper +{ + V&& x; + std::tuple fs; + + template + void operator()(I) + { + (void)std::get(std::move(fs))( + unsafe_get(std::forward(x))); + } +}; + +template< + typename R=deduced,typename V,typename... Fs, + typename ReturnType=return_type_by_index +> +ReturnType visit_by_index(V&& x,Fs&&... fs) +{ + using raw_variant=typename std::decay::type; + static_assert( + mp11::mp_size::value==sizeof...(Fs), + "the number of function objects must be the same as that of the " + "alternative types in the variant"); + + return mp11::mp_with_index::value>( + x.index(), + visit_by_index_helper{ + std::forward(x), + std::forward_as_tuple(std::forward(fs)...)}); +} + +template +struct relop_helper +{ + const fixed_variant &x,&y; + + template bool operator()(I)const + { + return RelOp{}(unsafe_get(x),unsafe_get(y)); + } +}; + +struct eq_ +{ + template + bool operator()(const T& x,const T& y)const{return x==y;} +}; + +template +bool operator==( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()==y.index()&& + mp11::mp_with_index(x.index(),relop_helper{x,y}); +} + +struct neq_ +{ + template + bool operator()(const T& x,const T& y)const{return x!=y;} +}; + +template +bool operator!=( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()!=y.index()|| + mp11::mp_with_index( + x.index(),relop_helper{x,y}); +} + +struct lt_ +{ + template + bool operator()(const T& x,const T& y)const{return x +bool operator<( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()( + x.index(),relop_helper{x,y})); +} + +struct lte_ +{ + template + bool operator()(const T& x,const T& y)const{return x<=y;} +}; + +template +bool operator<=( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()( + x.index(),relop_helper{x,y})); +} + +struct gt_ +{ + template + bool operator()(const T& x,const T& y)const{return x>y;} +}; + +template +bool operator>( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()>y.index()|| + (x.index()==y.index()&&mp11::mp_with_index( + x.index(),relop_helper{x,y})); +} + +struct gte_ +{ + template + bool operator()(const T& x,const T& y)const{return x>=y;} +}; + +template +bool operator>=( + const fixed_variant& x,const fixed_variant& y) +{ + return + x.index()>y.index()|| + (x.index()==y.index()&&mp11::mp_with_index( + x.index(),relop_helper{x,y})); +} + +} /* namespace poly_collection::fixed_variant_impl */ + +using boost::poly_collection::fixed_variant_impl::variant_size; + +#ifndef BOOST_NO_CXX14_VARIABLE_TEMPLATES +using boost::poly_collection::fixed_variant_impl::variant_size_v; +#endif + +using boost::poly_collection::fixed_variant_impl::bad_variant_access; +using boost::poly_collection::fixed_variant_impl::variant_alternative; +using boost::poly_collection::fixed_variant_impl::variant_alternative_t; +using boost::poly_collection::fixed_variant_impl::visit; +using boost::poly_collection::fixed_variant_impl::visit_by_index; +using boost::poly_collection::fixed_variant_impl::holds_alternative; +using boost::poly_collection::fixed_variant_impl::get; +using boost::poly_collection::fixed_variant_impl::unsafe_get; +using boost::poly_collection::fixed_variant_impl::get_if; + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/fixed_variant_iterator.hpp b/include/boost/poly_collection/detail/fixed_variant_iterator.hpp new file mode 100644 index 0000000..1dfbfd7 --- /dev/null +++ b/include/boost/poly_collection/detail/fixed_variant_iterator.hpp @@ -0,0 +1,219 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_ITERATOR_HPP +#define BOOST_POLY_COLLECTION_DETAIL_FIXED_VARIANT_ITERATOR_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +/* Iterator over the sequence of T subojects within a range of + * fixed_variant_closure>s. + */ + +template +class fixed_variant_alternative_iterator: + public boost::iterator_facade< + fixed_variant_alternative_iterator, + T, + boost::random_access_traversal_tag + > +{ + static constexpr std::size_t Stride=sizeof( + fixed_variant_impl::fixed_variant_closure< + typename std::remove_const::type,Variant>); + +public: + fixed_variant_alternative_iterator()=default; + fixed_variant_alternative_iterator(T* p)noexcept:p{p}{} + fixed_variant_alternative_iterator( + const fixed_variant_alternative_iterator&)=default; + fixed_variant_alternative_iterator& operator=( + const fixed_variant_alternative_iterator&)=default; + + template< + typename NonConstT, + typename std::enable_if< + std::is_same::value>::type* =nullptr + > + fixed_variant_alternative_iterator( + const fixed_variant_alternative_iterator& x)noexcept: + p{x.p}{} + + template< + typename NonConstT, + typename std::enable_if< + std::is_same::value>::type* =nullptr + > + fixed_variant_alternative_iterator& operator=( + const fixed_variant_alternative_iterator& x)noexcept + { + p=x.p; + return *this; + } + + /* interoperability with T* */ + + fixed_variant_alternative_iterator& operator=(T* p_)noexcept + {p=p_;return *this;} + operator T*()const noexcept{return p;} + + std::size_t stride()const noexcept{return Stride;} + +private: + template + friend class fixed_variant_alternative_iterator; + + using char_pointer=typename std::conditional< + std::is_const::value, + const char*, + char* + >::type; + + static char_pointer char_ptr(T* p)noexcept + {return reinterpret_cast(p);} + static T* value_ptr(char_pointer p)noexcept + {return reinterpret_cast(p);} + + friend class boost::iterator_core_access; + + T& dereference()const noexcept{return *p;} + bool equal(const fixed_variant_alternative_iterator& x)const noexcept + {return p==x.p;} + void increment()noexcept{p=value_ptr(char_ptr(p)+Stride);} + void decrement()noexcept{p=value_ptr(char_ptr(p)-Stride);} + template + void advance(Integral n)noexcept + {p=value_ptr(char_ptr(p)+n*(std::ptrdiff_t)Stride);} + std::ptrdiff_t distance_to( + const fixed_variant_alternative_iterator& x)const noexcept + {return (char_ptr(x.p)-char_ptr(p))/(std::ptrdiff_t)Stride;} + + T* p; +}; + +/* Iterator over the sequence of fixed_variant base objects within a range of + * fixed_variant_closure>s. As T is not part of the + * iterator definition, the stride between values is a run-time value. + */ + +template +class fixed_variant_iterator: + public boost::iterator_facade< + fixed_variant_iterator, + Variant, + boost::random_access_traversal_tag + > +{ +public: + fixed_variant_iterator()=default; + fixed_variant_iterator(Variant* p,std::size_t stride)noexcept: + p{p},stride_{stride}{} + fixed_variant_iterator(const fixed_variant_iterator&)=default; + fixed_variant_iterator& operator=(const fixed_variant_iterator&)=default; + + template< + typename NonConstVariant, + typename std::enable_if< + std::is_same::value>::type* =nullptr + > + fixed_variant_iterator( + const fixed_variant_iterator& x)noexcept: + p{x.p},stride_{x.stride_}{} + + template< + typename NonConstVariant, + typename std::enable_if< + std::is_same::value>::type* =nullptr + > + fixed_variant_iterator& operator=( + const fixed_variant_iterator& x)noexcept + { + p=x.p;stride_=x.stride_; + return *this; + } + + /* interoperability with Variant* */ + + fixed_variant_iterator& operator=(Variant* p_)noexcept{p=p_;return *this;} + operator Variant*()const noexcept{return p;} + + /* interoperability with fixed_variant_alternative_iterator */ + +#include + + template< + typename T, + typename NonConstT=typename std::remove_const::type, + typename NonConstVariant=typename std::remove_const::type, + typename std::enable_if< + mp11::mp_contains::value&& + (!std::is_const::value||std::is_const::value) + >::type* =nullptr + > + BOOST_POLY_COLLECTION_NO_SANITIZE explicit operator + fixed_variant_alternative_iterator()const noexcept + { + return p?std::addressof(unsafe_get(*p)):nullptr; + } + +#include + + std::size_t stride()const noexcept{return stride_;} + +private: + template + friend class fixed_variant_iterator; + + using char_pointer=typename std::conditional< + std::is_const::value, + const char*, + char* + >::type; + + static char_pointer char_ptr(Variant* p)noexcept + {return reinterpret_cast(p);} + static Variant* value_ptr(char_pointer p)noexcept + {return reinterpret_cast(p);} + + friend class boost::iterator_core_access; + + Variant& dereference()const noexcept{return *p;} + bool equal(const fixed_variant_iterator& x)const noexcept{return p==x.p;} + void increment()noexcept{p=value_ptr(char_ptr(p)+stride_);} + void decrement()noexcept{p=value_ptr(char_ptr(p)-stride_);} + template + void advance(Integral n)noexcept + {p=value_ptr(char_ptr(p)+n*(std::ptrdiff_t)stride_);} + std::ptrdiff_t distance_to(const fixed_variant_iterator& x)const noexcept + {return (char_ptr(x.p)-char_ptr(p))/(std::ptrdiff_t)stride_;} + + Variant* p; + std::size_t stride_; +}; + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/function_model.hpp b/include/boost/poly_collection/detail/function_model.hpp index 2d1d832..a3d0b79 100644 --- a/include/boost/poly_collection/detail/function_model.hpp +++ b/include/boost/poly_collection/detail/function_model.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2018 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -49,17 +49,22 @@ struct function_model { using value_type=callable_wrapper; + using type_index=std::type_info; + template using is_implementation=is_invocable_r; template using is_terminal=function_model_is_terminal; + template + static const std::type_info& index(){return typeid(T);} + template - static const std::type_info& subtypeid(const T&){return typeid(T);} + static const std::type_info& subindex(const T&){return typeid(T);} template - static const std::type_info& subtypeid( + static const std::type_info& subindex( const callable_wrapper& f) { return f.target_type(); diff --git a/include/boost/poly_collection/detail/functional.hpp b/include/boost/poly_collection/detail/functional.hpp index 6de521e..1cf1462 100644 --- a/include/boost/poly_collection/detail/functional.hpp +++ b/include/boost/poly_collection/detail/functional.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2018 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -23,7 +23,7 @@ * C++14 generic lambdas. */ -#if BOOST_WORKAROUND(BOOST_MSVC,>=1910) +#if BOOST_WORKAROUND(BOOST_MSVC,>=1910)&&BOOST_WORKAROUND(BOOST_MSVC,<1920) /* https://lists.boost.org/Archives/boost/2017/06/235687.php */ #define BOOST_POLY_COLLECTION_DEFINE_OVERLOAD_SET(name,f) \ @@ -66,7 +66,7 @@ struct tail_closure_class template return_type call(mp11::index_sequence,Args&&... args) { - return f(std::forward(args)...,std::get(t)...); + return f(std::forward(args)...,std::get(std::move(t))...); } template @@ -99,7 +99,7 @@ struct head_closure_class template return_type call(mp11::index_sequence,Args&&... args) { - return f(std::get(t)...,std::forward(args)...); + return f(std::get(std::move(t))...,std::forward(args)...); } template diff --git a/include/boost/poly_collection/detail/is_acceptable.hpp b/include/boost/poly_collection/detail/is_acceptable.hpp index 3b96c61..b132268 100644 --- a/include/boost/poly_collection/detail/is_acceptable.hpp +++ b/include/boost/poly_collection/detail/is_acceptable.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -13,6 +13,9 @@ #pragma once #endif +#include +#include +#include #include namespace boost{ @@ -21,20 +24,28 @@ namespace poly_collection{ namespace detail{ -/* This can be further specialized by Model when the std type trait classes - * fail to give the right info (as it can happen with class templates whose - * nominally existing operators do not compile for certain instantiations). +/* is_acceptable can be further specialized by (open collection) Model when + * the std type_traits classes fail to give the right info (as it can happen + * with class templates whose nominally existing operators do not compile for + * certain instantiations). */ template struct is_acceptable:std::integral_constant< bool, - Model::template is_implementation::value&& - std::is_move_constructible::type>::value&& - (std::is_move_assignable::type>::value|| - std::is_nothrow_move_constructible::type>::value) + Model::template is_implementation::value&&is_moveable::value >{}; +/* Closed collections are defined by having a compile-time fixed list of + * acceptable types. + */ + +template +struct is_acceptable< + T,Model, + typename std::enable_if::value>::type +>:mp11::mp_contains{}; + } /* namespace poly_collection::detail */ } /* namespace poly_collection */ diff --git a/include/boost/poly_collection/detail/is_closed_collection.hpp b/include/boost/poly_collection/detail/is_closed_collection.hpp new file mode 100644 index 0000000..0c30bf3 --- /dev/null +++ b/include/boost/poly_collection/detail/is_closed_collection.hpp @@ -0,0 +1,54 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_IS_CLOSED_COLLECTION_HPP +#define BOOST_POLY_COLLECTION_DETAIL_IS_CLOSED_COLLECTION_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +template +struct is_closed_collection:std::false_type{}; + +template +struct is_closed_collection< + Model,void_t +>:std::true_type +{ + using type_list=typename Model::acceptable_type_list; + + static_assert( + mp11::mp_is_set::value, + "all types in a closed collection must be distinct"); + + static_assert( + mp11::mp_all_of::value, + "all types of a closed collection must be nothrow move constructible " + "or else move constructible and move assignable"); +}; + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/is_equality_comparable.hpp b/include/boost/poly_collection/detail/is_equality_comparable.hpp index d4d6183..9105209 100644 --- a/include/boost/poly_collection/detail/is_equality_comparable.hpp +++ b/include/boost/poly_collection/detail/is_equality_comparable.hpp @@ -1,4 +1,4 @@ -/* Copyright 2017-2018 Joaquin M Lopez Munoz. +/* Copyright 2017-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -25,7 +25,7 @@ namespace detail{ template using is_equality_comparable=std::integral_constant< bool, - has_equal_to::value + has_equal_to::value >; } /* namespace poly_collection::detail */ diff --git a/include/boost/poly_collection/detail/is_moveable.hpp b/include/boost/poly_collection/detail/is_moveable.hpp new file mode 100644 index 0000000..9eeb2dd --- /dev/null +++ b/include/boost/poly_collection/detail/is_moveable.hpp @@ -0,0 +1,37 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_IS_MOVEABLE_HPP +#define BOOST_POLY_COLLECTION_DETAIL_IS_MOVEABLE_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +template struct is_moveable:std::integral_constant< + bool, + std::is_move_constructible::type>::value&& + (std::is_move_assignable::type>::value|| + std::is_nothrow_move_constructible::type>::value) +>{}; + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/is_nothrow_eq_comparable.hpp b/include/boost/poly_collection/detail/is_nothrow_eq_comparable.hpp index 7448b65..feafd3e 100644 --- a/include/boost/poly_collection/detail/is_nothrow_eq_comparable.hpp +++ b/include/boost/poly_collection/detail/is_nothrow_eq_comparable.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -34,7 +34,7 @@ struct is_nothrow_equality_comparable< >::type >:std::integral_constant< bool, - noexcept(std::declval()==std::declval()) + noexcept(std::declval()==std::declval()) >{}; } /* namespace poly_collection::detail */ diff --git a/include/boost/poly_collection/detail/iterator_impl.hpp b/include/boost/poly_collection/detail/iterator_impl.hpp index 2a40bbe..5111f61 100644 --- a/include/boost/poly_collection/detail/iterator_impl.hpp +++ b/include/boost/poly_collection/detail/iterator_impl.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2021 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -226,12 +226,25 @@ private: return BaseIterator{bit+(it-static_cast(bit))}; } - base_iterator base()const noexcept - {return local_iterator_impl::iterator_adaptor_::base();} - const std::type_info& type_info()const{return *mapit->first;} - segment_type& segment()noexcept - {return const_cast(mapit->second);} - const segment_type& segment()const noexcept{return mapit->second;} + base_iterator base()const noexcept + { + return local_iterator_impl::iterator_adaptor_::base(); + } + + const typename PolyCollection::type_index& type_info()const + { + return mapit->first; + } + + segment_type& segment()noexcept + { + return const_cast(mapit->second); + } + + const segment_type& segment()const noexcept + { + return mapit->second; + } const_segment_map_iterator mapit; }; diff --git a/include/boost/poly_collection/detail/iterator_traits.hpp b/include/boost/poly_collection/detail/iterator_traits.hpp index f6be219..43dcb7b 100644 --- a/include/boost/poly_collection/detail/iterator_traits.hpp +++ b/include/boost/poly_collection/detail/iterator_traits.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -31,7 +31,8 @@ namespace detail{ /* (Internal) bunch of traits-grouped functions for const-preserving * interoperatibility between iterators and local iterators of a - * poly_collection. + * poly_collection, plus access to associated model's index from + * an iterator type. */ template @@ -53,6 +54,8 @@ template struct iterator_traits { using container_type=typename poly_collection_of::type; + using model_type=typename model_of::type; + using type_index=typename container_type::type_index; using is_const_iterator=typename std::is_const< typename std::remove_reference< typename std::iterator_traits::reference @@ -93,10 +96,7 @@ struct iterator_traits static local_base_iterator local_base_iterator_from(iterator it)noexcept { - return { - it.mapit, - model_of::type::nonconst_iterator(it.segpos) - }; + return {it.mapit,model_type::nonconst_iterator(it.segpos)}; } static iterator @@ -105,6 +105,12 @@ struct iterator_traits { return {lbit.mapit,mapend.base(),lbit.base()}; } + + template + static decltype(model_type::template index()) index() + { + return model_type::template index(); + } }; } /* namespace poly_collection::detail */ diff --git a/include/boost/poly_collection/detail/packed_segment.hpp b/include/boost/poly_collection/detail/packed_segment.hpp index 584d6a0..d4903f3 100644 --- a/include/boost/poly_collection/detail/packed_segment.hpp +++ b/include/boost/poly_collection/detail/packed_segment.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2020 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -35,13 +35,23 @@ namespace detail{ * {value_type*,sizeof(store_value_type)}. * - Model provides a function value_ptr for * const Concrete* -> const value_type* conversion. + * - If Model provides a final_type template alias, it is this + * type that is stored in the segment rather than Concrete. In this case, + * final_type must be constructible from Concrete and + * Concrete* must be reinterpret_castable to final_type* and vice + * versa. */ template class packed_segment:public segment_backend { + template + static typename M::template final_type final_type_helper(M); + static Concrete final_type_helper(...); + using value_type=typename Model::value_type; - using store_value_type=value_holder; + using final_type=decltype(final_type_helper(std::declval())); + using store_value_type=value_holder; using store=std::vector< store_value_type, typename std::allocator_traits:: @@ -266,7 +276,7 @@ private: static Concrete* concrete_ptr(store_value_type* p)noexcept { return reinterpret_cast( - static_cast*>(p)); + static_cast*>(p)); } static const Concrete* const_concrete_ptr(const store_value_type* p)noexcept @@ -282,8 +292,8 @@ private: static const store_value_type* const_store_value_type_ptr( const Concrete* p)noexcept { - return static_cast*>( - reinterpret_cast*>(p)); + return static_cast( + reinterpret_cast*>(p)); } /* It would have sufficed if iterator_from returned const_store_iterator diff --git a/include/boost/poly_collection/detail/poly_collection.hpp b/include/boost/poly_collection/detail/poly_collection.hpp index 491ce2d..9895995 100644 --- a/include/boost/poly_collection/detail/poly_collection.hpp +++ b/include/boost/poly_collection/detail/poly_collection.hpp @@ -16,17 +16,20 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include #include #include -#include +#include #include #include #include -#include #include namespace boost{ @@ -42,13 +45,18 @@ using namespace detail; template class poly_collection { - template - static const std::type_info& subtypeid(const T& x) - {return Model::subtypeid(x);} + /* used only to early force closed collection acceptability checks */ + static constexpr bool is_closed_collection= + detail::is_closed_collection::value; + template struct for_all_types{using type=void*;}; template using for_all=typename for_all_types::type; + template + using enable_if_open_collection=typename std::enable_if< + !detail::is_closed_collection::value + >::type*; template struct is_implementation: /* using makes VS2015 choke, hence we derive */ Model::template is_implementation::type>{}; @@ -116,14 +124,6 @@ class poly_collection template using const_segment_iterator= typename segment_type::template const_iterator; - using segment_map=type_info_map< - segment_type, - typename std::allocator_traits::template - rebind_alloc - >; - using segment_map_allocator_type=typename segment_map::allocator_type; - using segment_map_iterator=typename segment_map::iterator; - using const_segment_map_iterator=typename segment_map::const_iterator; public: /* types */ @@ -136,8 +136,24 @@ public: using const_reference=const value_type&; using pointer=typename std::allocator_traits::pointer; using const_pointer=typename std::allocator_traits::const_pointer; + using type_index=typename Model::type_index; private: + using segment_map=typename detail::segment_map< + type_index, + segment_type, + typename std::allocator_traits::template + rebind_alloc + >; + using segment_map_allocator_type=typename segment_map::allocator_type; + using segment_map_iterator=typename segment_map::iterator; + using const_segment_map_iterator=typename segment_map::const_iterator; + template + static auto index()->decltype(Model::template index()) + {return Model::template index();} + template + static auto subindex(const T& x)->decltype(Model::subindex(x)) + {return Model::subindex(x);} template friend class detail::iterator_impl; template @@ -183,7 +199,7 @@ public: template const_local_iterator cend()const noexcept{return end();} - const std::type_info& type_info()const{return *it->first;} + const type_index& type_info()const{return it->first;} protected: friend class poly_collection; @@ -377,21 +393,38 @@ public: /* construct/destroy/copy */ - poly_collection()=default; + poly_collection(){initialize_map();}; + poly_collection(const poly_collection&)=default; - poly_collection(poly_collection&&)=default; + + poly_collection(poly_collection&& x):poly_collection{x.get_allocator()} + { + map.swap(x.map); + } + explicit poly_collection(const allocator_type& al): - map{segment_map_allocator_type{al}}{} + map{segment_map_allocator_type{al}} + { + initialize_map(); + } + poly_collection(const poly_collection& x,const allocator_type& al): map{x.map,segment_map_allocator_type{al}}{} - poly_collection(poly_collection&& x,const allocator_type& al): - map{std::move(x.map),segment_map_allocator_type{al}}{} + + poly_collection(poly_collection&& x,const allocator_type& al):map{al} + { + segment_map m2{x.get_allocator()}; + initialize_map(m2); + m2.swap(x.map); + segment_map m3{std::move(m2),al}; + map.swap(m3); + } template poly_collection( InputIterator first,InputIterator last, const allocator_type& al=allocator_type{}): - map{segment_map_allocator_type{al}} + poly_collection{segment_map_allocator_type{al}} { this->insert(first,last); } @@ -399,37 +432,53 @@ public: // TODO: what to do with initializer_list? poly_collection& operator=(const poly_collection&)=default; - poly_collection& operator=(poly_collection&&)=default; + + poly_collection& operator=(poly_collection&& x) + { + if(this!=std::addressof(x)){ + segment_map m2{x.get_allocator()}; + initialize_map(m2); + m2.swap(x.map); + map=std::move(m2); + } + return *this; + } allocator_type get_allocator()const noexcept{return map.get_allocator();} - /* type registration */ + /* type registration (open collections only) */ template< typename... T, - for_all...> =nullptr + for_all...> =nullptr, + typename M=Model, + enable_if_open_collection =nullptr > void register_types() { - /* http://twitter.com/SeanParent/status/558765089294020609 */ - - using seq=int[1+sizeof...(T)]; - (void)seq{ - 0, - (map.insert( - typeid(T),segment_type::template make(get_allocator())),0)... - }; + mp11::mp_for_each< + mp11::mp_transform> + >(create_segment{map}); } - bool is_registered(const std::type_info& info)const + template< + typename M=Model, + enable_if_open_collection =nullptr + > + bool is_registered(const type_index& info)const { return map.find(info)!=map.end(); } - template =nullptr> + template< + typename T, + enable_if_acceptable =nullptr, + typename M=Model, + enable_if_open_collection =nullptr + > bool is_registered()const { - return is_registered(typeid(T)); + return is_registered(index()); } /* iterators */ @@ -441,60 +490,60 @@ public: const_iterator cbegin()const noexcept{return begin();} const_iterator cend()const noexcept{return end();} - local_base_iterator begin(const std::type_info& info) + local_base_iterator begin(const type_index& info) { auto it=get_map_iterator_for(info); return {it,segment(it).begin()}; } - local_base_iterator end(const std::type_info& info) + local_base_iterator end(const type_index& info) { auto it=get_map_iterator_for(info); return {it,segment(it).end()}; } - const_local_base_iterator begin(const std::type_info& info)const + const_local_base_iterator begin(const type_index& info)const { auto it=get_map_iterator_for(info); return {it,segment(it).begin()}; } - const_local_base_iterator end(const std::type_info& info)const + const_local_base_iterator end(const type_index& info)const { auto it=get_map_iterator_for(info); return {it,segment(it).end()}; } - const_local_base_iterator cbegin(const std::type_info& info)const + const_local_base_iterator cbegin(const type_index& info)const {return begin(info);} - const_local_base_iterator cend(const std::type_info& info)const + const_local_base_iterator cend(const type_index& info)const {return end(info);} template =nullptr> local_iterator begin() { - auto it=get_map_iterator_for(typeid(T)); + auto it=get_map_iterator_for(index()); return {it,segment(it).template begin()}; } template =nullptr> local_iterator end() { - auto it=get_map_iterator_for(typeid(T)); + auto it=get_map_iterator_for(index()); return {it,segment(it).template end()}; } template =nullptr> const_local_iterator begin()const { - auto it=get_map_iterator_for(typeid(T)); + auto it=get_map_iterator_for(index()); return {it,segment(it).template begin()}; } template =nullptr> const_local_iterator end()const { - auto it=get_map_iterator_for(typeid(T)); + auto it=get_map_iterator_for(index()); return {it,segment(it).template end()}; } @@ -504,21 +553,22 @@ public: template =nullptr> const_local_iterator cend()const{return end();} - base_segment_info segment(const std::type_info& info) + base_segment_info segment(const type_index& info) { return get_map_iterator_for(info); } - const_base_segment_info segment(const std::type_info& info)const + const_base_segment_info segment(const type_index& info)const { return get_map_iterator_for(info); } template =nullptr> - segment_info segment(){return get_map_iterator_for(typeid(T));} + segment_info segment(){return get_map_iterator_for(index());} template =nullptr> - const_segment_info segment()const{return get_map_iterator_for(typeid(T));} + const_segment_info segment()const + {return get_map_iterator_for(index());} segment_traversal_info segment_traversal()noexcept{return map;} const_segment_traversal_info segment_traversal()const noexcept{return map;} @@ -531,7 +581,7 @@ public: return true; } - bool empty(const std::type_info& info)const + bool empty(const type_index& info)const { return segment(get_map_iterator_for(info)).empty(); } @@ -539,7 +589,7 @@ public: template =nullptr> bool empty()const { - return segment(get_map_iterator_for(typeid(T))).template empty(); + return segment(get_map_iterator_for(index())).template empty(); } size_type size()const noexcept @@ -549,7 +599,7 @@ public: return res; } - size_type size(const std::type_info& info)const + size_type size(const type_index& info)const { return segment(get_map_iterator_for(info)).size(); } @@ -557,10 +607,10 @@ public: template =nullptr> size_type size()const { - return segment(get_map_iterator_for(typeid(T))).template size(); + return segment(get_map_iterator_for(index())).template size(); } - size_type max_size(const std::type_info& info)const + size_type max_size(const type_index& info)const { return segment(get_map_iterator_for(info)).max_size(); } @@ -568,10 +618,10 @@ public: template =nullptr> size_type max_size()const { - return segment(get_map_iterator_for(typeid(T))).template max_size(); + return segment(get_map_iterator_for(index())).template max_size(); } - size_type capacity(const std::type_info& info)const + size_type capacity(const type_index& info)const { return segment(get_map_iterator_for(info)).capacity(); } @@ -579,7 +629,7 @@ public: template =nullptr> size_type capacity()const { - return segment(get_map_iterator_for(typeid(T))).template capacity(); + return segment(get_map_iterator_for(index())).template capacity(); } void reserve(size_type n) @@ -587,7 +637,7 @@ public: for(auto& x:map)x.second.reserve(n); } - void reserve(const std::type_info& info,size_type n) + void reserve(const type_index& info,size_type n) { segment(get_map_iterator_for(info)).reserve(n); } @@ -605,7 +655,7 @@ public: for(auto& x:map)x.second.shrink_to_fit(); } - void shrink_to_fit(const std::type_info& info) + void shrink_to_fit(const type_index& info) { segment(get_map_iterator_for(info)).shrink_to_fit(); } @@ -613,7 +663,7 @@ public: template =nullptr> void shrink_to_fit() { - segment(get_map_iterator_for(typeid(T))).template shrink_to_fit(); + segment(get_map_iterator_for(index())).template shrink_to_fit(); } /* modifiers */ @@ -653,7 +703,7 @@ public: local_base_iterator emplace_pos(const_local_base_iterator pos,Args&&... args) { - BOOST_ASSERT(pos.type_info()==typeid(T)); + BOOST_ASSERT(pos.type_info()==index()); return { pos.mapit, pos.segment().template emplace(pos.base(),std::forward(args)...) @@ -709,7 +759,7 @@ public: nonconst_version> insert(local_iterator_impl pos,T&& x) { - BOOST_ASSERT(pos.type_info()==subtypeid(x)); + BOOST_ASSERT(pos.type_info()==subindex(x)); return { pos.mapit, pos.segment().insert(pos.base(),std::forward(x)) @@ -846,7 +896,7 @@ public: size_type n=0; for(;first!=last;++first){ - BOOST_ASSERT(pos.type_info()==subtypeid(*first)); + BOOST_ASSERT(pos.type_info()==subindex(*first)); it=std::next(seg.insert(it,*first)); ++n; } @@ -940,7 +990,7 @@ public: for(auto& x:map)x.second.clear(); } - void clear(const std::type_info& info) + void clear(const type_index& info) { segment(get_map_iterator_for(info)).clear(); } @@ -948,7 +998,7 @@ public: template =nullptr> void clear() { - segment(get_map_iterator_for(typeid(T))).template clear(); + segment(get_map_iterator_for(index())).template clear(); } void swap(poly_collection& x){map.swap(x.map);} @@ -958,6 +1008,71 @@ private: friend bool operator==( const poly_collection&,const poly_collection&); + struct create_segment + { + segment_map& map; + + template + void operator()(TI) + { + using T=typename TI::type; + map.insert( + index(),segment_type::template make(map.get_allocator())); + } + }; + + void initialize_map(){initialize_map(map);} + + void initialize_map(segment_map& m) + { + initialize_map( + m, + std::integral_constant< + bool,detail::is_closed_collection::value>{}); + } + + void initialize_map(segment_map&,std::false_type /* open collection */){} + + void initialize_map(segment_map& m,std::true_type /* closed collection */) + { + mp11::mp_for_each< + mp11::mp_transform< + mp11::mp_identity,typename Model::acceptable_type_list + > + >(create_segment{m}); + } + + static const std::type_info& type_info(const std::type_info& info) + { + return info; + } + + template + static const std::type_info& type_info(const TypeIndex& info) + { + return typeid(void); /* no way to recover the type from its index */ + } + + template + static const std::type_info& subtype_info(const T& x) + { + return subtype_info(x,std::is_same{}); + } + + template + static const std::type_info& subtype_info( + const T& x,std::true_type /* type_index is std::type_info*/) + { + return subindex(x); + } + + template + static const std::type_info& subtype_info( + const T& x,std::false_type /* type_index is not std::type_info*/) + { + return Model::subtype_info(x); + } + template< typename T, enable_if_acceptable =nullptr, @@ -965,12 +1080,12 @@ private: > const_segment_map_iterator get_map_iterator_for(const T& x) { - const auto& id=subtypeid(x); + const auto& id=subindex(x); auto it=map.find(id); if(it!=map.end())return it; - else if(id!=typeid(T))throw unregistered_type{id}; + else if(id!=index())throw unregistered_type{subtype_info(x)}; else return map.insert( - typeid(T),segment_type::template make(get_allocator())).first; + index(),segment_type::template make(get_allocator())).first; } template< @@ -980,10 +1095,10 @@ private: > const_segment_map_iterator get_map_iterator_for(const T&) { - auto it=map.find(typeid(T)); + auto it=map.find(index()); if(it!=map.end())return it; else return map.insert( - typeid(T),segment_type::template make(get_allocator())).first; + index(),segment_type::template make(get_allocator())).first; } template< @@ -993,9 +1108,9 @@ private: > const_segment_map_iterator get_map_iterator_for(const T& x)const { - const auto& id=subtypeid(x); + const auto& id=subindex(x); auto it=map.find(id); - if(it==map.end())throw unregistered_type{id}; + if(it==map.end())throw unregistered_type{subtype_info(x)}; return it; } @@ -1016,7 +1131,7 @@ private: const_segment_map_iterator get_map_iterator_for( const T& x,const segment_type& seg) { - const auto& id=subtypeid(x); + const auto& id=subindex(x); auto it=map.find(id); if(it!=map.end())return it; else return map.insert( @@ -1026,23 +1141,22 @@ private: template const_segment_map_iterator get_map_iterator_for() { - auto it=map.find(typeid(T)); + auto it=map.find(index()); if(it!=map.end())return it; else return map.insert( - typeid(T),segment_type::template make(get_allocator())).first; + index(),segment_type::template make(get_allocator())).first; } - const_segment_map_iterator get_map_iterator_for(const std::type_info& info) + const_segment_map_iterator get_map_iterator_for(const type_index& info) { return const_cast(this)-> get_map_iterator_for(info); } - const_segment_map_iterator get_map_iterator_for( - const std::type_info& info)const + const_segment_map_iterator get_map_iterator_for(const type_index& info)const { auto it=map.find(info); - if(it==map.end())throw unregistered_type{info}; + if(it==map.end())throw unregistered_type{type_info(info)}; return it; } @@ -1067,7 +1181,7 @@ private: > segment_base_iterator push_back(segment_type& seg,T&& x) { - return subtypeid(x)==typeid(T)? + return subindex(x)==index()? seg.push_back_terminal(std::forward(x)): seg.push_back(std::forward(x)); } @@ -1090,7 +1204,7 @@ private: static segment_base_iterator local_insert( segment_type& seg,BaseIterator pos,U&& x) { - BOOST_ASSERT(subtypeid(x)==typeid(T)); + BOOST_ASSERT(subindex(x)==index()); return seg.insert(pos,std::forward(x)); } @@ -1102,7 +1216,7 @@ private: static segment_base_iterator local_insert( segment_type& seg,BaseIterator pos,U&& x) { - if(subtypeid(x)==typeid(T))return seg.insert(pos,std::forward(x)); + if(subindex(x)==index())return seg.insert(pos,std::forward(x)); else return seg.template emplace(pos,std::forward(x)); } @@ -1143,7 +1257,7 @@ bool operator==( const auto &mapx=x.map,&mapy=y.map; for(const auto& p:mapx){ auto ss=p.second.size(); - auto it=mapy.find(*p.first); + auto it=mapy.find(p.first); if(it==mapy.end()?ss!=0:p.second!=it->second)return false; s+=ss; } diff --git a/include/boost/poly_collection/detail/segment.hpp b/include/boost/poly_collection/detail/segment.hpp index a080839..4cd5d3f 100644 --- a/include/boost/poly_collection/detail/segment.hpp +++ b/include/boost/poly_collection/detail/segment.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2018 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -175,6 +175,12 @@ public: impl().nv_insert(it,*static_cast(subaddress(x)))); } + template + base_iterator insert(iterator it,const T& x) + { + return insert(const_iterator{it},x); + } + template< typename T, typename std::enable_if< @@ -198,6 +204,17 @@ public: impl().nv_insert(it,std::move(*static_cast(subaddress(x))))); } + template< + typename U,typename T, + typename std::enable_if< + !std::is_lvalue_reference::value&&!std::is_const::value + >::type* =nullptr + > + base_iterator insert(iterator it,T&& x) + { + return insert(const_iterator{it},std::move(x)); + } + template base_iterator insert(InputIterator first,InputIterator last) { diff --git a/include/boost/poly_collection/detail/segment_map.hpp b/include/boost/poly_collection/detail/segment_map.hpp new file mode 100644 index 0000000..40ee8c3 --- /dev/null +++ b/include/boost/poly_collection/detail/segment_map.hpp @@ -0,0 +1,46 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_SEGMENT_MAP_HPP +#define BOOST_POLY_COLLECTION_DETAIL_SEGMENT_MAP_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +template struct segment_map_helper; + +template<> struct segment_map_helper +{ + template using fn=type_info_map; +}; + +template<> struct segment_map_helper +{ + template using fn=size_t_map; +}; + +template +using segment_map=typename segment_map_helper::template fn; + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/segment_split.hpp b/include/boost/poly_collection/detail/segment_split.hpp index 2cc9e18..e7dc981 100644 --- a/include/boost/poly_collection/detail/segment_split.hpp +++ b/include/boost/poly_collection/detail/segment_split.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -13,11 +13,16 @@ #pragma once #endif +#include #include #include -#include #include +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + namespace boost{ namespace poly_collection{ @@ -32,16 +37,17 @@ class segment_splitter using traits=iterator_traits; using local_base_iterator=typename traits::local_base_iterator; using base_segment_info_iterator=typename traits::base_segment_info_iterator; + using type_index=typename traits::type_index; public: struct info { - const std::type_info& type_info()const noexcept{return *pinfo_;} - local_base_iterator begin()const noexcept{return begin_;} - local_base_iterator end()const noexcept{return end_;} + const type_index& type_info()const noexcept{return info_;} + local_base_iterator begin()const noexcept{return begin_;} + local_base_iterator end()const noexcept{return end_;} - const std::type_info* pinfo_; - local_base_iterator begin_,end_; + const type_index& info_; + local_base_iterator begin_,end_; }; struct iterator:iterator_facade @@ -65,7 +71,7 @@ public: info dereference()const noexcept { return { - &it->type_info(), + it->type_info(), it==traits::base_segment_info_iterator_from(*pfirst)? traits::local_base_iterator_from(*pfirst):it->begin(), it==traits::base_segment_info_iterator_from(*plast)? @@ -111,7 +117,7 @@ segment_split( /* equivalent to for(auto i:segment_split(first,last))f(i) */ template -void for_each_segment( +BOOST_FORCEINLINE void for_each_segment( const PolyCollectionIterator& first,const PolyCollectionIterator& last,F&& f) { using traits=iterator_traits; @@ -125,20 +131,20 @@ void for_each_segment( if(sfirst!=slast){ for(;;){ - f(info{&sfirst->type_info(),lbfirst,sfirst->end()}); + f(info{sfirst->type_info(),lbfirst,sfirst->end()}); ++sfirst; if(sfirst==slast)break; lbfirst=sfirst->begin(); } - if(sfirst!=send)f(info{&sfirst->type_info(),sfirst->begin(),lblast}); + if(sfirst!=send)f(info{sfirst->type_info(),sfirst->begin(),lblast}); } else if(sfirst!=send){ - f(info{&sfirst->type_info(),lbfirst,lblast}); + f(info{sfirst->type_info(),lbfirst,lblast}); } } #else template -void for_each_segment( +BOOST_FORCEINLINE void for_each_segment( const PolyCollectionIterator& first,const PolyCollectionIterator& last,F&& f) { for(auto i:segment_split(first,last))f(i); @@ -151,4 +157,8 @@ void for_each_segment( } /* namespace boost */ +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + #endif diff --git a/include/boost/poly_collection/detail/size_t_map.hpp b/include/boost/poly_collection/detail/size_t_map.hpp new file mode 100644 index 0000000..b5b71a5 --- /dev/null +++ b/include/boost/poly_collection/detail/size_t_map.hpp @@ -0,0 +1,100 @@ +/* Copyright 2024 Joaquin M Lopez Munoz. + * Distributed under 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/poly_collection for library home page. + */ + +#ifndef BOOST_POLY_COLLECTION_DETAIL_SIZE_T_MAP_HPP +#define BOOST_POLY_COLLECTION_DETAIL_SIZE_T_MAP_HPP + +#if defined(_MSC_VER) +#pragma once +#endif + +#include +#include +#include +#include +#include + +namespace boost{ + +namespace poly_collection{ + +namespace detail{ + +/* Map-like wrapper over a vector of std::pair. It is required + * that the first member of the i-th element be i, which implies that + * insert(i,x) must executed in increasing order of i. + */ + +template +class size_t_map +{ + using vector_type=std::vector< + std::pair, + typename std::allocator_traits::template + rebind_alloc> + >; + +public: + using key_type=std::size_t; + using mapped_type=T; + using value_type=typename vector_type::value_type; + using allocator_type=typename vector_type::allocator_type; + using iterator=typename vector_type::iterator; + using const_iterator=typename vector_type::const_iterator; + + size_t_map()=default; + size_t_map(const size_t_map& x)=default; + size_t_map(size_t_map&& x)=default; + size_t_map(const allocator_type& al):v{al}{} + size_t_map(const size_t_map& x,const allocator_type& al):v{x.v,al}{} + size_t_map(size_t_map&& x,const allocator_type& al):v{std::move(x.v),al}{} + size_t_map& operator=(const size_t_map& x)=default; + size_t_map& operator=(size_t_map&& x)=default; + + allocator_type get_allocator()const noexcept{return v.get_allocator();} + + iterator begin()noexcept{return v.begin();} + iterator end()noexcept{return v.end();} + const_iterator begin()const noexcept{return v.begin();} + const_iterator end()const noexcept{return v.end();} + const_iterator cbegin()const noexcept{return v.cbegin();} + const_iterator cend()const noexcept{return v.cend();} + + const_iterator find(const key_type& key)const + { + if(key + std::pair insert(const key_type& key,P&& x) + { + BOOST_ASSERT(key==v.size()); + v.emplace_back(key,std::forward

(x)); + return {v.end()-1,true}; + } + + void swap(size_t_map& x){v.swap(x.v);} + +private: + vector_type v; +}; + +template +void swap(size_t_map& x,size_t_map& y) +{ + x.swap(y); +} + +} /* namespace poly_collection::detail */ + +} /* namespace poly_collection */ + +} /* namespace boost */ + +#endif diff --git a/include/boost/poly_collection/detail/stride_iterator.hpp b/include/boost/poly_collection/detail/stride_iterator.hpp index ec0de42..01a98f0 100644 --- a/include/boost/poly_collection/detail/stride_iterator.hpp +++ b/include/boost/poly_collection/detail/stride_iterator.hpp @@ -13,6 +13,7 @@ #pragma once #endif +#include #include #include #include @@ -63,32 +64,7 @@ public: stride_iterator& operator=(Value* p_)noexcept{p=p_;return *this;} operator Value*()const noexcept{return p;} - template< - typename DerivedValue, - typename std::enable_if< - std::is_base_of::value&& - (std::is_const::value||!std::is_const::value) - >::type* =nullptr - > - explicit stride_iterator(DerivedValue* x)noexcept: - p{x},stride_{sizeof(DerivedValue)}{} - -#if BOOST_WORKAROUND(BOOST_GCC_VERSION,>=40900)||\ - BOOST_WORKAROUND(BOOST_CLANG,>=1)&&\ - (__clang_major__>3 || __clang_major__==3 && __clang_minor__ >= 8) -/* https://github.com/boostorg/poly_collection/issues/15 */ - -#define BOOST_POLY_COLLECTION_NO_SANITIZE - -/* UBSan seems not to be supported in some environments */ -#if defined(BOOST_GCC_VERSION) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wattributes" -#elif defined(BOOST_CLANG) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wattributes" -#endif -#endif +#include template< typename DerivedValue, @@ -97,21 +73,11 @@ public: (!std::is_const::value||std::is_const::value) >::type* =nullptr > -#if defined(BOOST_POLY_COLLECTION_NO_SANITIZE) - __attribute__((no_sanitize("undefined"))) -#endif + BOOST_POLY_COLLECTION_NO_SANITIZE explicit operator DerivedValue*()const noexcept {return static_cast(p);} -#if defined(BOOST_POLY_COLLECTION_NO_SANITIZE) -#if defined(BOOST_GCC_VERSION) -#pragma GCC diagnostic pop -#elif defined(BOOST_CLANG) -#pragma clang diagnostic pop -#endif - -#undef BOOST_POLY_COLLECTION_NO_SANITIZE -#endif +#include std::size_t stride()const noexcept{return stride_;} diff --git a/include/boost/poly_collection/detail/type_info_map.hpp b/include/boost/poly_collection/detail/type_info_map.hpp index f983b82..54e41e3 100644 --- a/include/boost/poly_collection/detail/type_info_map.hpp +++ b/include/boost/poly_collection/detail/type_info_map.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2020 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -32,33 +32,34 @@ namespace detail{ * std::type_info instances to describe the same type, which implies that * std::type_info::operator== and std::type_info::hash_code are costly * operations typically relying on the stored type name. - * type_info_ptr_hash behaves roughly as a + * type_info_map behaves roughly as a * std::unordered_map but maintains an internal cache of * passed std::type_info instances so that lookup is performed (when there's a * cache hit) without invoking std::type_info equality and hashing ops. */ -struct type_info_ptr_hash +struct type_info_hash { - std::size_t operator()(const std::type_info* p)const noexcept - {return p->hash_code();} + std::size_t operator()(const std::type_info& i)const noexcept + {return i.hash_code();} }; -struct type_info_ptr_equal_to +struct type_info_equal_to { bool operator()( - const std::type_info* p,const std::type_info* q)const noexcept - {return *p==*q;} + const std::type_info& i,const std::type_info& j)const noexcept + {return i==j;} }; template class type_info_map { using map_type=std::unordered_map< - const std::type_info*,T, - type_info_ptr_hash,type_info_ptr_equal_to, + std::reference_wrapper,T, + type_info_hash,type_info_equal_to, typename std::allocator_traits::template - rebind_alloc> + rebind_alloc< + std::pair,T>> >; public: @@ -147,7 +148,7 @@ public: { auto cit=cache.find(&key); if(cit!=cache.end())return cit->second; - auto mit=map.find(&key); + auto mit=map.find(key); if(mit!=map.end())cache.insert({&key,mit}); return mit; } @@ -156,14 +157,14 @@ public: { auto cit=cache.find(&key); if(cit!=cache.end())return cit->second; - return map.find(&key); + return map.find(key); } template std::pair insert(const key_type& key,P&& x) { auto c=map.bucket_count(); - auto p=map.emplace(&key,std::forward

(x)); + auto p=map.emplace(key,std::forward

(x)); if(map.bucket_count()!=c)rebuild_cache(); cache.insert({&key,p.first}); return p; @@ -231,12 +232,12 @@ private: void build_cache(const cache_type& x) { - for(const auto& p:x)cache.insert({p.first,map.find(p.first)}); + for(const auto& p:x)cache.insert({p.first,map.find(*p.first)}); } void rebuild_cache() { - for(auto& p:cache)p.second=map.find(p.first); + for(auto& p:cache)p.second=map.find(*p.first); } map_type map; diff --git a/include/boost/poly_collection/detail/type_restitution.hpp b/include/boost/poly_collection/detail/type_restitution.hpp index 3cc298a..111223c 100644 --- a/include/boost/poly_collection/detail/type_restitution.hpp +++ b/include/boost/poly_collection/detail/type_restitution.hpp @@ -1,4 +1,4 @@ -/* Copyright 2016-2017 Joaquin M Lopez Munoz. +/* Copyright 2016-2024 Joaquin M Lopez Munoz. * Distributed under 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) @@ -13,45 +13,145 @@ #pragma once #endif +#include +#include +#include +#include +#include +#include +#include #include #include -#include +#include #include +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + namespace boost{ namespace poly_collection{ +struct all_types; + namespace detail{ -/* Given types Ts..., a const std::type_info& info and a local_base_iterator - * it, we denote by restitute(info,it): - * - a local_iterator from it, if info==typeid(Ti) for some Ti in Ts... - * - it otherwise. - * - * Using this notation, restitute_range(f,args...)(s) resolves to - * f(restitute(info,begin),restitute(info,end),args...) where - * info=s.type_info(), begin=s.begin(), end=s.end(). +/* If a list of restituted types contain all_types, replaces it with the + * list of all acceptable types of the associated closed collection. */ -template +template +struct restitution_list_impl +{ + static_assert( + !mp11::mp_contains::value, + "all_types can't be used with open collections"); + + using type=L; +}; + +template +struct restitution_list_impl< + Model,L, + typename std::enable_if::value>::type +> +{ + using type=typename std::conditional< + mp11::mp_contains::value, + typename Model::acceptable_type_list, + L + >::type; +}; + +template +using restitution_list= + typename restitution_list_impl>::type; + +/* Calculates if the given types cover the entire set of acceptable types for + * the associated closed collection. + */ + +namespace is_total_restitution_impl +{ +template +struct helper:std::false_type{}; + +#if BOOST_WORKAROUND(BOOST_MSVC,<=1900) +template +struct is_contained; + +template