diff --git a/include/boost/openmethod/core.hpp b/include/boost/openmethod/core.hpp index 11739064..6c678e1e 100644 --- a/include/boost/openmethod/core.hpp +++ b/include/boost/openmethod/core.hpp @@ -2792,10 +2792,6 @@ using boost::openmethod::virtual_ptr; #ifdef __MRDOCS__ -namespace detail { -struct unspecified; -} - //! Blueprint for a specialization of @ref virtual_traits (exposition only). //! //! Specializations of @ref virtual_traits must implement the members listed diff --git a/include/boost/openmethod/initialize.hpp b/include/boost/openmethod/initialize.hpp index 014d2647..7f829d84 100644 --- a/include/boost/openmethod/initialize.hpp +++ b/include/boost/openmethod/initialize.hpp @@ -182,7 +182,17 @@ struct generic_compiler { return nullptr; } + std::deque classes; + + auto classes_begin() const { + return classes.begin(); + } + + auto classes_end() const { + return classes.end(); + } + std::vector methods; std::size_t class_mark = 0; bool compilation_done = false; @@ -190,15 +200,12 @@ struct generic_compiler { template struct trace_stream { - trace_stream(bool trace) : trace(trace) { - } - - bool trace; + bool on = false; std::size_t indentation_level{0}; auto operator++() -> trace_stream& { if constexpr (Compiler::has_trace) { - if (trace) { + if (on) { for (std::size_t i = 0; i < indentation_level; ++i) { Compiler::Registry::output::os << " "; } @@ -296,7 +303,7 @@ struct range; template auto write_range(trace_stream& tr, range range, F fn) -> auto& { if constexpr (Compiler::has_trace) { - if (tr.trace) { + if (tr.on) { tr << "("; const char* sep = ""; for (auto value : range) { @@ -314,7 +321,7 @@ auto write_range(trace_stream& tr, range range, F fn) -> auto& { template auto operator<<(trace_stream& tr, const T& value) -> auto& { if constexpr (Compiler::has_trace) { - if (tr.trace) { + if (tr.on) { Compiler::Registry::output::os << value; } } @@ -324,7 +331,7 @@ auto operator<<(trace_stream& tr, const T& value) -> auto& { template auto operator<<(trace_stream& tr, const rflush& rf) -> auto& { if constexpr (Compiler::has_trace) { - if (tr.trace) { + if (tr.on) { std::size_t digits = 1; auto tmp = rf.value / 10; @@ -349,7 +356,7 @@ template auto operator<<(trace_stream& tr, const boost::dynamic_bitset<>& bits) -> auto& { if constexpr (Compiler::has_trace) { - if (tr.trace) { + if (tr.on) { auto i = bits.size(); while (i != 0) { --i; @@ -388,9 +395,6 @@ template struct registry::compiler : detail::generic_compiler { using type_index_type = decltype(rtti::type_index(0)); - static constexpr bool use_n2216 = - mp11::mp_contains, n2216>::value; - typename detail::aggregate_reports, policy_list>::type report; @@ -425,10 +429,15 @@ struct registry::compiler : detail::generic_compiler { is_more_specific(const overrider* a, const overrider* b) -> bool; static auto is_base(const overrider* a, const overrider* b) -> bool; - std::tuple opts; - static constexpr bool has_trace = - mp11::mp_contains, openmethod::trace>::value; - bool trace = false; + std::tuple options; + + template + static constexpr bool has_option = + mp11::mp_contains, Option>::value; + + static constexpr bool has_trace = has_option; + static constexpr bool has_n2216 = has_option; + mutable detail::trace_stream tr; using indent = typename detail::trace_stream::indent; }; @@ -467,11 +476,42 @@ void registry::compiler::initialize() { registry::initialized = true; } +#ifdef _MSC_VER +namespace detail { + +template +struct msvc_tuple_get; + +template +struct msvc_tuple_get { + template + static decltype(auto) fn(const Tuple& t) { + return std::get(t); + } +}; + +template +struct msvc_tuple_get { + template + static decltype(auto) fn(const Tuple&) { + return T(); + } +}; +} // namespace detail +#endif + template template registry::compiler::compiler(Options... opts) - : opts(opts...), trace(detail::option(this->opts).on), - tr(trace) { + : options(opts...) { + if constexpr (has_trace) { +#ifdef _MSC_VER + tr.on = detail::msvc_tuple_get::fn(options).on; +#else + // Even with the constexpr has_trace guard, msvc errors on this. + tr.on = std::get(options).on; +#endif + } } template @@ -1148,7 +1188,7 @@ void registry::compiler::build_dispatch_table( m.dispatch_table.push_back(&m.not_implemented); ++m.report.not_implemented; } else { - if constexpr (!use_n2216) { + if constexpr (!has_option) { if (remaining > 1) { ++tr << "ambiguous\n"; m.dispatch_table.push_back(&m.ambiguous); @@ -1209,7 +1249,7 @@ void registry::compiler::build_dispatch_table( select_dominant_overriders(overriders, pick, remaining); - if constexpr (!use_n2216) { + if constexpr (!has_option) { if (remaining > 1) { ++tr << "ambiguous 'next'\n"; overrider->next = &m.ambiguous; @@ -1368,7 +1408,7 @@ void registry::compiler::write_global_data() { ++tr << rflush(4, dispatch_data_size) << " " << gv_iter << " end\n"; if constexpr (has_vptr) { - vptr::initialize(classes.begin(), classes.end(), opts); + vptr::initialize(*this, options); } new_dispatch_data.swap(dispatch_data); @@ -1407,7 +1447,7 @@ void registry::compiler::select_dominant_overriders( return; } - if constexpr (use_n2216) { + if constexpr (has_option) { if (!candidates[pick]->covariant_return_type) { return; } @@ -1571,10 +1611,6 @@ void registry::compiler::print( //! @endcode template inline auto initialize(Options&&... options) { - static_assert( - (std::is_base_of_v && ...), - "invalid option type"); - if (detail::odr_check::count > 1) { // Multiple definitions of default_registry detected. // This indicates an ODR violation. diff --git a/include/boost/openmethod/policies/fast_perfect_hash.hpp b/include/boost/openmethod/policies/fast_perfect_hash.hpp index 6015cf73..e91f8f86 100644 --- a/include/boost/openmethod/policies/fast_perfect_hash.hpp +++ b/include/boost/openmethod/policies/fast_perfect_hash.hpp @@ -80,10 +80,10 @@ struct fast_perfect_hash : type_hash { static void check(std::size_t index, type_id type); - template + template static void initialize( - ForwardIterator first, ForwardIterator last, - std::vector& buckets, std::tuple options); + const InitializeContext& ctx, std::vector& buckets, + const std::tuple& options); public: //! Find the hash factors @@ -95,25 +95,18 @@ struct fast_perfect_hash : type_hash { //! If no suitable values are found, calls the error handler with //! a @ref hash_error object then calls `abort`. //! - //! @tparam ForwardIterator An iterator to a range of @ref - //! IdsToVptr objects. - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. - //! @param first An iterator to the beginning of the range. - //! @param last An iterator to the end of the range. - //! @param options Zero or more option objects. + //! @tparam Context An @ref InitializeContext. + //! @param ctx A Context object. //! @return A pair containing the minimum and maximum hash values. - template - static auto initialize( - ForwardIterator first, ForwardIterator last, - std::tuple options) { + template + static auto + initialize(const Context& ctx, const std::tuple& options) { if constexpr (Registry::has_runtime_checks) { initialize( - first, last, detail::fast_perfect_hash_control, - options); + ctx, detail::fast_perfect_hash_control, options); } else { std::vector buckets; - initialize(first, last, buckets, options); + initialize(ctx, buckets, options); } return std::pair{min_value, max_value}; @@ -150,7 +143,7 @@ struct fast_perfect_hash : type_hash { //! arguments. //! @param options Zero or more option objects. template - static auto finalize(std::tuple) -> void { + static auto finalize(const std::tuple&) -> void { detail::fast_perfect_hash_control.clear(); } }; @@ -169,21 +162,16 @@ template std::size_t fast_perfect_hash::fn::max_value; template -template +template void fast_perfect_hash::fn::initialize( - ForwardIterator first, ForwardIterator last, std::vector& buckets, - std::tuple opts) { - using namespace policies; - - const auto N = std::distance(first, last); - (void)opts; - - if constexpr ( - detail::has_option && Registry::has_output) { - if (detail::option(opts).on) { - Registry::output::os << "Finding hash factor for " << N - << " types\n"; - } + const InitializeContext& ctx, std::vector& buckets, + const std::tuple& options) { + (void)options; + + const auto N = std::distance(ctx.classes_begin(), ctx.classes_end()); + + if constexpr (mp11::mp_contains, trace>::value) { + Registry::output::os << "Finding hash factor for " << N << " types\n"; } std::default_random_engine rnd(13081963); @@ -202,13 +190,9 @@ void fast_perfect_hash::fn::initialize( min_value = (std::numeric_limits::max)(); max_value = (std::numeric_limits::min)(); - if constexpr ( - mp11::mp_contains, trace>::value && - Registry::has_output) { - if (detail::option(opts).on) { - Registry::output::os << " trying with M = " << M << ", " - << hash_size << " buckets\n"; - } + if constexpr (InitializeContext::template has_option) { + ctx.tr << " trying with M = " << M << ", " << hash_size + << " buckets\n"; } std::size_t attempts = 0; @@ -221,7 +205,8 @@ void fast_perfect_hash::fn::initialize( ++total_attempts; mult = uniform_dist(rnd) | 1; - for (auto iter = first; iter != last; ++iter) { + for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); + ++iter) { for (auto type_iter = iter->type_id_begin(); type_iter != iter->type_id_end(); ++type_iter) { auto type = *type_iter; @@ -238,15 +223,10 @@ void fast_perfect_hash::fn::initialize( } } - if constexpr ( - mp11::mp_contains, trace>::value && - Registry::has_output) { - if (detail::option(opts).on) { - Registry::output::os - << " found " << mult << " after " << total_attempts - << " attempts; span = [" << min_value << ", " - << max_value << "]\n"; - } + if constexpr (InitializeContext::template has_option) { + ctx.tr << " found " << mult << " after " << total_attempts + << " attempts; span = [" << min_value << ", " + << max_value << "]\n"; } return; diff --git a/include/boost/openmethod/policies/vptr_map.hpp b/include/boost/openmethod/policies/vptr_map.hpp index 64b24e29..c26e5de2 100644 --- a/include/boost/openmethod/policies/vptr_map.hpp +++ b/include/boost/openmethod/policies/vptr_map.hpp @@ -38,20 +38,17 @@ class vptr_map : public vptr { public: //! Stores the v-table pointers. //! - //! @tparam ForwardIterator An iterator to a range of @ref - //! IdsToVptr objects. - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. - //! @param first An iterator to the beginning of the range. - //! @param last An iterator to the end of the range. - //! @param options Zero or more option objects. - template - static void initialize( - ForwardIterator first, ForwardIterator last, - std::tuple) { + //! @tparam Context An @ref InitializeContext. + //! @tparam Options... Zero or more option types. + //! @param ctx A Context object. + //! @param options A tuple of option objects. + template + static void + initialize(const Context& ctx, const std::tuple&) { decltype(vptrs) new_vptrs; - for (auto iter = first; iter != last; ++iter) { + for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); + ++iter) { for (auto type_iter = iter->type_id_begin(); type_iter != iter->type_id_end(); ++type_iter) { @@ -109,11 +106,11 @@ class vptr_map : public vptr { //! Clears the map. //! - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. - //! @param options Zero or more option objects. + //! @tparam Options... Zero or more option types. + //! @param ctx A Context object. + //! @param options A tuple of option objects. template - static auto finalize(std::tuple) -> void { + static auto finalize(const std::tuple&) -> void { vptrs.clear(); } }; diff --git a/include/boost/openmethod/policies/vptr_vector.hpp b/include/boost/openmethod/policies/vptr_vector.hpp index 4d9bf9a6..814231a7 100644 --- a/include/boost/openmethod/policies/vptr_vector.hpp +++ b/include/boost/openmethod/policies/vptr_vector.hpp @@ -58,29 +58,24 @@ struct vptr_vector : vptr { //! function is called. Its result determines the size of the vector. //! The v-table pointers are copied into the vector. //! - //! @tparam ForwardIterator An iterator to a range of @ref - //! IdsToVptr objects. - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. - //! @param first An iterator to the beginning of the range. - //! @param last An iterator to the end of the range. - //! @param options Zero or more option objects. - template + //! @tparam Context An @ref InitializeContext. + //! @tparam Options... Zero or more option types. + //! @param ctx A Context object. + //! @param options A tuple of option objects. + template static auto initialize( - ForwardIterator first, ForwardIterator last, - std::tuple opts) -> void { - (void)opts; + const Context& ctx, const std::tuple& options) -> void { std::size_t size; + (void)options; if constexpr (has_type_hash) { - auto [_, max_value] = - type_hash::template initialize( - first, last, opts); + auto [_, max_value] = type_hash::initialize(ctx, options); size = max_value + 1; } else { size = 0; - for (auto iter = first; iter != last; ++iter) { + for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); + ++iter) { for (auto type_iter = iter->type_id_begin(); type_iter != iter->type_id_end(); ++type_iter) { size = (std::max)(size, std::size_t(*type_iter)); @@ -96,7 +91,8 @@ struct vptr_vector : vptr { detail::vptr_vector_vptrs.resize(size); } - for (auto iter = first; iter != last; ++iter) { + for (auto iter = ctx.classes_begin(); iter != ctx.classes_end(); + ++iter) { for (auto type_iter = iter->type_id_begin(); type_iter != iter->type_id_end(); ++type_iter) { std::size_t index; @@ -173,13 +169,13 @@ struct vptr_vector : vptr { } } - //! Clears the vector. + //! Releases the memory allocated by `initialize`. //! - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. + //! @tparam Options... Zero or more option types, deduced from the function + //! arguments. //! @param options Zero or more option objects. template - static auto finalize(std::tuple) -> void { + static auto finalize(const std::tuple&) -> void { using namespace policies; if constexpr (Registry::has_indirect_vptr) { diff --git a/include/boost/openmethod/preamble.hpp b/include/boost/openmethod/preamble.hpp index 055cfbd9..edda369a 100644 --- a/include/boost/openmethod/preamble.hpp +++ b/include/boost/openmethod/preamble.hpp @@ -368,6 +368,8 @@ struct deferred_overrider_info : overrider_info { virtual void resolve_type_ids() = 0; }; +struct unspecified {}; + } // namespace detail #ifdef __MRDOCS__ @@ -385,28 +387,6 @@ struct LightweightOutputStream { #endif -namespace detail { - -template -constexpr bool has_option = - mp11::mp_contains, Target>::value; - -template -inline auto option(std::tuple opts) { - constexpr auto index = - mp11::mp_find, Target>::value; - - if constexpr (index == sizeof...(Options)) { - return Target(); - } else { - return std::get(opts); - } -} - -struct option_base {}; - -} // namespace detail - //! N2216 ambiguity resolution. //! //! If `n2216` is present in @ref initialize\'s `Options`, additional steps are @@ -422,7 +402,7 @@ struct option_base {}; //! - Otherwise, pick one of the overriders. Which one is used is unspecified, //! but remains the same throughtout the program, and across different runs of //! the same program. -struct n2216 : detail::option_base {}; +struct n2216 {}; //! Enable `initialize` tracing. //! @@ -437,7 +417,7 @@ struct n2216 : detail::option_base {}; //! The content of the trace is neither specified, nor stable across versions. //! It is comprehensive, and useful for troubleshooting missing class //! registrations, missing or ambiguous overriders, etc. -struct trace : detail::option_base { +struct trace { //! Enable trace if `true`. bool on = true; @@ -477,23 +457,48 @@ namespace policies { #ifdef __MRDOCS__ -//! Type ids and v-table pointers for a class (exposition only). -struct IdsToVptr { - //! Returns an iterator to the beginning of a range of `type_id`s for a - //! single registered class. - auto type_id_begin() const; +//! Class information for initializing a policy (exposition only). +//! +//! Provides the v-table pointer for a class, identified by one or more type +//! ids, via the members described on this page. +struct InitializeClass { + //! Beginning of a range of type ids for a class. + //! + //! @return A forward iterator to the beginning of a range of type ids for + //! a class. + auto type_id_begin() const -> detail::unspecified; - //! Returns an iterator to the end of a range of `type_id`s for a - //! single registered class. - auto type_id_end() const; + //! End of a range of type ids for a class. + //! + //! @return A forward iterator to the end of a range of type ids for a + //! class. + auto type_id_end() const -> detail::unspecified; - //! Returns a range of `type_id`s that this assignment applies to. + //! Reference to the v-table pointer for the class. + //! + //! @return A reference to the v-table pointer for the class. auto vptr() const -> const vptr_type&; }; -#endif +//! Context for initializing a policy (exposition only). +//! +//! @ref initialize passes a "context" object, of unspecified type, to the +//! `initialize` functions of the policies that have one. It provides the +//! v-table pointer for the registered classes, via the members described on +//! this page. +struct InitializeContext { + //! Beginning of a range of `InitializeClass` objects. + //! + //! @return A forward iterator to the beginning of a range of @ref + //! InitializeClass objects. + detail::unspecified classes_begin() const; -#ifdef __MRDOCS__ + //! End of a range of `InitializeClass` objects. + //! + //! @return A forward iterator to the end of a range of @ref + //! InitializeClass objects. + detail::unspecified classes_end() const; +}; //! Blueprint for @ref rtti metafunctions (exposition only). template @@ -588,11 +593,6 @@ struct rtti { }; }; -#ifdef __MRDOCS__ -struct std_rtti; -struct static_rtti; -#endif - //! Policy for deferred type id collection. //! //! Some custom RTTI systems rely on static constructors to assign type ids. @@ -643,17 +643,10 @@ struct VptrFn { //! Called by @ref registry::initialize to let the policy store the v-table //! pointer associated to each `type_id`. //! - //! @tparam ForwardIterator An iterator to a range of @ref - //! IdsToVptr objects. - //! @tparam Options... Zero or more option types, deduced from the - //! function arguments. - //! @param first An iterator to the beginning of the range. - //! @param last An iterator to the end of the range. - //! @param options Zero or more option objects. - template - static auto initialize( - ForwardIterator first, ForwardIterator last, - std::tuple opts); + //! @tparam Context A class that conforms to the @ref InitializeContext + //! blueprint. + template + static auto initialize(const Context& ctx) -> void; //! Return a *reference* to a v-table pointer for an object. //! @@ -712,7 +705,7 @@ struct TypeHashFn { //! Initialize the hash table. //! //! @tparam ForwardIterator An iterator to a range of @ref - //! IdsToVptr objects. + //! InitializeClass objects. //! @tparam Options... Zero or more option types, deduced from the //! function arguments. //! @param first An iterator to the beginning of the range. diff --git a/test/test_shared_virtual_ptr_value_semantics.cpp b/test/test_shared_virtual_ptr_value_semantics.cpp index 75b4baaf..acb76cfc 100644 --- a/test/test_shared_virtual_ptr_value_semantics.cpp +++ b/test/test_shared_virtual_ptr_value_semantics.cpp @@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(cast_shared_ptr_lvalue_reference) { bool cast_moves() { std::shared_ptr animal = std::make_shared(); - (void) std::static_pointer_cast(animal); + (void)std::static_pointer_cast(animal); return animal.get() == nullptr; }