diff --git a/include/yorel/yomm2/core.hpp b/include/yorel/yomm2/core.hpp index 2dae31ed..1a23c85e 100644 --- a/include/yorel/yomm2/core.hpp +++ b/include/yorel/yomm2/core.hpp @@ -39,7 +39,7 @@ using virtual_ptr_policy = std::conditional_t< // ----------------------------------------------------------------------------- // Method -template +template struct method; template @@ -136,7 +136,7 @@ struct method : detail::method_info { Policy, declared_argument_types, parameter_types>>; info.vp_begin = spec_type_ids::begin; info.vp_end = spec_type_ids::end; - fn.specs.push_front(info); + fn.specs.push_back(info); } }; @@ -430,7 +430,7 @@ method::method() { this->not_implemented = (void*)not_implemented_handler; this->ambiguous = (void*)ambiguous_handler; this->method_type = Policy::template static_type(); - Policy::methods.push_front(*this); + Policy::methods.push_back(*this); } template @@ -691,14 +691,14 @@ auto update() -> detail::compiler; inline error_handler_type set_error_handler(error_handler_type handler) { auto p = &default_policy::error; - auto prev= default_policy::error; + auto prev = default_policy::error; default_policy::error = handler; return prev; } inline method_call_error_handler set_method_call_error_handler(method_call_error_handler handler) { - auto prev= default_policy::call_error; + auto prev = default_policy::call_error; default_policy::call_error = handler; return prev; } diff --git a/include/yorel/yomm2/detail.hpp b/include/yorel/yomm2/detail.hpp index e01bb246..f5207ac8 100644 --- a/include/yorel/yomm2/detail.hpp +++ b/include/yorel/yomm2/detail.hpp @@ -85,16 +85,6 @@ struct runtime; namespace mp11 = boost::mp11; -enum { TRACE_RUNTIME = 1, TRACE_CALLS = 2 }; - -#if defined(YOMM2_SHARED) -extern yOMM2_API std::ostream* logs; -extern yOMM2_API unsigned trace_flags; -#else -inline std::ostream* logs; -inline unsigned trace_flags; -#endif - template struct parameter_type_list; @@ -177,51 +167,6 @@ const char* default_method_name() { #endif } -// ----------------------------------------------------------------------------- -// iterator adapter for passing range from external_vpt to fast_perfect_hash - -template -class pair_first_iterator { - PairIterator iter; - - public: - using iterator_category = typename std::forward_iterator_tag; - using difference_type = typename PairIterator::difference_type; - using value_type = decltype(std::declval()->first); - using pointer = const value_type*; - using reference = const value_type&; - - explicit pair_first_iterator(PairIterator iter) : iter(iter) { - } - - reference operator*() const { - return iter->first; - } - - pointer operator->() const { - return &iter->first; - } - - pair_first_iterator& operator++() { - ++iter; - return *this; - } - - pair_first_iterator operator++(int) const { - return pair_first_iterator(iter++); - } - - friend bool - operator==(const pair_first_iterator& a, const pair_first_iterator& b) { - return a.iter == b.iter; - } - - friend bool - operator!=(const pair_first_iterator& a, const pair_first_iterator& b) { - return a.iter != b.iter; - } -}; - // ----------------------------------------------------------------------------- // class info @@ -258,7 +203,7 @@ struct class_declaration_aux> this->type = collect_static_type_id(); this->first_base = type_id_list>::begin; this->last_base = type_id_list>::end; - Policy::classes.push_front(*this); + Policy::classes.push_back(*this); this->is_abstract = std::is_abstract_v; this->static_vptr = &Policy::template static_vptr; } diff --git a/include/yorel/yomm2/detail/chain.hpp b/include/yorel/yomm2/detail/chain.hpp index 7f41bb76..e2fe9678 100644 --- a/include/yorel/yomm2/detail/chain.hpp +++ b/include/yorel/yomm2/detail/chain.hpp @@ -2,7 +2,7 @@ #define YOREL_YOMM2_CHAIN_INCLUDED #include -#include +#include namespace yorel { namespace yomm2 { @@ -13,69 +13,78 @@ class static_chain { static_chain(static_chain&) = delete; static_chain() = default; - explicit static_chain(int) : first(nullptr), removed_prev(nullptr) { - } - class static_link { public: static_link(const static_link&) = delete; static_link() = default; - explicit static_link(int) : _next(nullptr) { - } T* next() { - return _next; + return next_ptr; } protected: friend class static_chain; - T* _next; + T* prev_ptr; + T* next_ptr; }; - struct link : static_link { - link() : static_link(0) { + void push_back(T& node) { + assert(node.prev_ptr == nullptr); + assert(node.next_ptr == nullptr); + + if (!first) { + first = &node; + node.prev_ptr = &node; + return; } - }; - void push_front(T& node) { - assert(node._next == nullptr); - node._next = first; - first = &node; + auto last = first->prev_ptr; + last->next_ptr = &node; + node.prev_ptr = last; + first->prev_ptr = &node; } void remove(T& node) { - iterator iter; + BOOST_ASSERT(first != nullptr); - if (&node == first) { - first = node._next; - node._next = nullptr; - removed_prev = nullptr; - return; - } + auto prev = node.prev_ptr; + auto next = node.next_ptr; + auto last = first->prev_ptr; - if (removed_prev != nullptr && removed_prev != &node) { - iter = - std::find_if(iterator(removed_prev), end(), [&node](T& other) { - return other._next == &node; - }); - if (iter == end()) { - iter = std::find_if( - begin(), iterator(removed_prev), - [&node](T& other) { return other._next == &node; }); + node.prev_ptr = nullptr; + node.next_ptr = nullptr; + + if (&node == last) { + if (&node == first) { + first = nullptr; + return; } - } else { - iter = std::find_if(begin(), end(), [&node](T& other) { - return other._next == &node; - }); + + first->prev_ptr = prev; + prev->next_ptr = nullptr; + return; } - if (iter == end()) { - assert(false); - abort(); + if (&node == first) { + first = next; + first->prev_ptr = last; + return; } - iter->_next = node._next; - removed_prev = &*iter; + prev->next_ptr = next; + next->prev_ptr = prev; + } + + void clear() { + auto next = first; + first = nullptr; + + while (next) { + auto cur = next; + next = cur->next_ptr; + cur->prev_ptr = nullptr; + cur->next_ptr = nullptr; + } } class iterator { @@ -100,7 +109,7 @@ class static_chain { iterator& operator++() { assert(ptr); - ptr = ptr->_next; + ptr = ptr->next_ptr; return *this; } @@ -151,7 +160,7 @@ class static_chain { const_iterator& operator++() { assert(ptr); - ptr = ptr->_next; + ptr = ptr->next_ptr; return *this; } @@ -192,16 +201,6 @@ class static_chain { protected: T* first; - T* removed_prev; -}; - -template -class chain : public static_chain { - public: - chain() { - this->first = nullptr; - this->removed_prev = nullptr; - } }; } // namespace detail diff --git a/include/yorel/yomm2/detail/compiler.hpp b/include/yorel/yomm2/detail/compiler.hpp index 2b6a40d5..62b0e07b 100644 --- a/include/yorel/yomm2/detail/compiler.hpp +++ b/include/yorel/yomm2/detail/compiler.hpp @@ -44,18 +44,18 @@ struct generic_compiler { }; struct class_ { - bool is_abstract{false}; + bool is_abstract = false; std::vector type_ids; std::vector transitive_bases; std::vector direct_bases; std::vector direct_derived; - std::unordered_set compatible_classes; + std::unordered_set covariant_classes; std::vector used_by_vp; - int next_slot{0}; - int first_used_slot{-1}; - int layer{0}; - std::size_t mark{0}; // temporary mark to detect cycles - std::size_t weight{0}; // number of proper direct or indirect bases + boost::dynamic_bitset<> used_slots; + boost::dynamic_bitset<> reserved_slots; + std::size_t first_slot = 0; + std::size_t mark = 0; // temporary mark to detect cycles + std::size_t weight = 0; // number of proper direct or indirect bases std::vector vtbl; std::uintptr_t** static_vptr; @@ -115,7 +115,7 @@ struct generic_compiler { std::deque classes; std::vector methods; - std::size_t class_visit = 0; + std::size_t class_mark = 0; bool compilation_done = false; }; @@ -147,6 +147,29 @@ trace_type& operator<<( return trace; } +struct spec_name { + spec_name( + const generic_compiler::method& method, + const generic_compiler::definition* def) + : method(method), def(def) { + } + const generic_compiler::method& method; + const generic_compiler::definition* def; +}; + +template +trace_type& operator<<(trace_type& trace, const spec_name& sn) { + if (sn.def == &sn.method.ambiguous) { + trace << "ambiguous"; + } else if (sn.def == &sn.method.not_implemented) { + trace << "not implemented"; + } else { + trace << type_name(sn.def->info->type); + } + + return trace; +} + template struct compiler : generic_compiler { using policy_type = Policy; @@ -165,19 +188,17 @@ struct compiler : generic_compiler { void resolve_static_type_ids(); void augment_classes(); - void calculate_compatible_classes(class_& cls); + void calculate_covariant_classes(class_& cls); void augment_methods(); - std::vector layer_classes(); - void allocate_slots(); - void allocate_slot_down(class_* cls, std::size_t slot); - void allocate_slot_up(class_* cls, std::size_t slot); + void assign_slots(); + void assign_tree_slots(class_& cls, std::size_t base_slot); + void assign_lattice_slots(class_& cls); void build_dispatch_tables(); void build_dispatch_table( method& m, std::size_t dim, std::vector::const_iterator group, const bitvec& candidates, bool concrete); void install_gv(); - void optimize(); void print(const update_method_report& report) const; static std::vector best(std::vector& candidates); @@ -197,11 +218,6 @@ struct compiler : generic_compiler { static constexpr bool trace_enabled = Policy::template has_facet; using indent = typename trace_type::indent; - - template - static void write_spec_name( - const generic_compiler::method& method, - const generic_compiler::definition* def, Stream& trace); }; compiler() -> compiler; @@ -213,7 +229,6 @@ void compiler::install_global_tables() { } install_gv(); - optimize(); print(report); ++trace << "Finished\n"; @@ -224,7 +239,7 @@ auto compiler::compile() { resolve_static_type_ids(); augment_classes(); augment_methods(); - allocate_slots(); + assign_slots(); build_dispatch_tables(); compilation_done = true; @@ -303,14 +318,10 @@ void compiler::augment_classes() { // type_info object per class. However, it guarantees that the // type_index for a class has a unique value. for (auto& cr : Policy::classes) { - if constexpr (trace_enabled) { - { - indent _(trace); - ++trace << type_name(cr.type) << ": " - << range{cr.first_base, cr.last_base}; - - ++trace << "\n"; - } + { + indent _(trace); + ++trace << type_name(cr.type) << ": " + << range{cr.first_base, cr.last_base} << "\n"; } auto& rtc = class_map[Policy::type_index(cr.type)]; @@ -367,11 +378,11 @@ void compiler::augment_classes() { // At this point bases may contain duplicates, and also indirect // bases. Clean that up. - std::size_t mark = ++class_visit; + std::size_t mark = ++class_mark; for (auto& rtc : classes) { decltype(rtc.transitive_bases) bases; - mark = ++class_visit; + mark = ++class_mark; for (auto rtb : rtc.transitive_bases) { if (rtb->mark != mark) { @@ -392,7 +403,7 @@ void compiler::augment_classes() { std::sort( rtc.transitive_bases.begin(), rtc.transitive_bases.end(), [](auto a, auto b) { return a->weight > b->weight; }); - mark = ++class_visit; + mark = ++class_mark; // Collect the direct base classes. The first base is certainly a // direct one. Remove *its* bases from the candidates, by marking @@ -419,11 +430,12 @@ void compiler::augment_classes() { } for (auto& rtc : classes) { - calculate_compatible_classes(rtc); + calculate_covariant_classes(rtc); } if constexpr (trace_enabled) { ++trace << "Inheritance lattice:\n"; + for (auto& rtc : classes) { indent _(trace); ++trace << rtc << "\n"; @@ -432,30 +444,29 @@ void compiler::augment_classes() { indent _(trace); ++trace << "bases: " << rtc.direct_bases << "\n"; ++trace << "derived: " << rtc.direct_derived << "\n"; - ++trace << "compatible: " << rtc.compatible_classes << "\n"; + ++trace << "covariant: " << rtc.covariant_classes << "\n"; } } } } template -void compiler::calculate_compatible_classes(class_& cls) { - if (!cls.compatible_classes.empty()) { +void compiler::calculate_covariant_classes(class_& cls) { + if (!cls.covariant_classes.empty()) { return; } - cls.compatible_classes.insert(&cls); + cls.covariant_classes.insert(&cls); for (auto derived : cls.direct_derived) { - if (derived->compatible_classes.empty()) { - calculate_compatible_classes(*derived); + if (derived->covariant_classes.empty()) { + calculate_covariant_classes(*derived); } std::copy( - derived->compatible_classes.begin(), - derived->compatible_classes.end(), - std::inserter( - cls.compatible_classes, cls.compatible_classes.end())); + derived->covariant_classes.begin(), + derived->covariant_classes.end(), + std::inserter(cls.covariant_classes, cls.covariant_classes.end())); } } @@ -472,15 +483,14 @@ void compiler::augment_methods() { auto meth_iter = methods.begin(); for (auto& meth_info : Policy::methods) { - if constexpr (trace_enabled) { - ++trace << meth_info.name << " " - << range{meth_info.vp_begin, meth_info.vp_end} << "\n"; - } + ++trace << meth_info.name << " " + << range{meth_info.vp_begin, meth_info.vp_end} << "\n"; indent _(trace); meth_iter->info = &meth_info; meth_iter->vp.reserve(meth_info.arity()); + meth_iter->slots.resize(meth_info.arity()); std::size_t param_index = 0; for (auto ti : range{meth_info.vp_begin, meth_info.vp_end}) { @@ -506,12 +516,14 @@ void compiler::augment_methods() { meth_iter->ambiguous.pf = reinterpret_cast(meth_iter->info->ambiguous); meth_iter->ambiguous.method_index = method_index; - meth_iter->ambiguous.spec_index = meth_info.specs.size(); + auto spec_size = meth_info.specs.size(); + meth_iter->ambiguous.spec_index = spec_size; meth_iter->not_implemented.pf = reinterpret_cast(meth_iter->info->not_implemented); meth_iter->not_implemented.method_index = method_index; - meth_iter->not_implemented.spec_index = meth_info.specs.size() + 1; - meth_iter->specs.resize(meth_info.specs.size()); + meth_iter->not_implemented.spec_index = spec_size + 1; + + meth_iter->specs.resize(spec_size); auto spec_iter = meth_iter->specs.begin(); for (auto& definition_info : meth_info.specs) { @@ -560,150 +572,162 @@ void compiler::augment_methods() { } } -template -std::vector compiler::layer_classes() { - ++trace << "Layering classes...\n"; - - std::vector input; - input.reserve(classes.size()); - std::transform( - classes.begin(), classes.end(), std::back_inserter(input), - [](class_& cls) { return &cls; }); - - std::vector layered; - layered.reserve(classes.size()); - - for (int layer = 1; !input.empty(); ++layer) { - indent _(trace, 1); - ++trace; - - for (auto class_iter = input.begin(); class_iter != input.end();) { - auto seen_all_bases = true; - auto in_this_layer = (*class_iter)->direct_bases.empty(); - - for (auto base : (*class_iter)->direct_bases) { - if (!base->layer) { - seen_all_bases = false; - break; - } else if (base->layer == layer) { - in_this_layer = false; - break; - } - if (base->layer == layer - 1) { - in_this_layer = true; - } - } - - if (seen_all_bases && in_this_layer) { - layered.push_back(*class_iter); - (*class_iter)->layer = layer; +namespace detail { - if constexpr (trace_enabled) { - trace << " " << **class_iter; - } +inline void merge_into(boost::dynamic_bitset<>& a, boost::dynamic_bitset<>& b) { + if (b.size() < a.size()) { + b.resize(a.size()); + } - class_iter = input.erase(class_iter); - } else { - ++class_iter; - } + for (std::size_t i = 0; i < a.size(); ++i) { + if (a[i]) { + b[i] = true; } - trace << "\n"; + } +} + +inline void set_bit(boost::dynamic_bitset<>& mask, std::size_t bit) { + if (bit >= mask.size()) { + mask.resize(bit + 1); } - return std::move(layered); + mask[bit] = true; } -template -void compiler::allocate_slots() { - auto layered = layer_classes(); +} // namespace detail +template +void compiler::assign_slots() { ++trace << "Allocating slots...\n"; - indent _(trace); - for (auto cls : layered) { - for (const auto& mp : cls->used_by_vp) { - std::size_t slot = cls->next_slot++; + { + indent _(trace); - ++trace << mp.method->info->name << "#" << mp.param << ": slot " - << slot << "\n"; - indent _(trace); - ++trace << *cls; + ++class_mark; - if (mp.method->slots.size() <= mp.param) { - mp.method->slots.resize(mp.param + 1); + for (auto& cls : classes) { + if (cls.direct_bases.size() == 0) { + if (std::find_if( + cls.covariant_classes.begin(), + cls.covariant_classes.end(), [](auto cls) { + return cls->direct_bases.size() > 1; + }) == cls.covariant_classes.end()) { + assign_tree_slots(cls, 0); + } else { + assign_lattice_slots(cls); + } } + } + } - mp.method->slots[mp.param] = slot; - - if (cls->first_used_slot == -1) { - cls->first_used_slot = slot; - } + ++trace << "Allocating MI v-tables...\n"; - cls->mark = ++class_visit; + { + indent _(trace); - for (auto derived : cls->direct_derived) { - allocate_slot_down(derived, slot); + for (auto& cls : classes) { + if (cls.used_slots.empty()) { + // not involved in multiple inheritance + continue; } - ++trace << "\n"; + auto first_slot = cls.used_slots.find_first(); + cls.first_slot = + first_slot == boost::dynamic_bitset<>::npos ? 0 : first_slot; + cls.vtbl.resize(cls.used_slots.size() - cls.first_slot); + ++trace << cls << " vtbl: " << cls.first_slot << "-" + << cls.used_slots.size() << " slots " << cls.used_slots + << "\n"; } } +} + +template +void compiler::assign_tree_slots(class_& cls, std::size_t base_slot) { + auto next_slot = base_slot; + + for (const auto& mp : cls.used_by_vp) { + mp.method->slots[mp.param] = next_slot++; + } + + cls.first_slot = 0; + cls.vtbl.resize(next_slot); - for (auto& c : classes) { - c.vtbl.resize(c.next_slot); + for (auto pd : cls.direct_derived) { + assign_tree_slots(*pd, next_slot); } } template -void compiler::allocate_slot_down(class_* cls, std::size_t slot) { - - if (cls->mark == class_visit) +void compiler::assign_lattice_slots(class_& cls) { + if (cls.mark == class_mark) { return; + } - cls->mark = class_visit; + cls.mark = class_mark; - trace << " " << *cls; + if (!cls.used_by_vp.empty()) { + for (const auto& mp : cls.used_by_vp) { + ++trace << " in " << cls << " for " << mp.method->info->name + << " parameter " << mp.param << "\n"; - assert(slot >= cls->next_slot); + indent _(trace); - cls->next_slot = slot + 1; + ++trace << "reserved slots: " << cls.reserved_slots + << " used slots: " << cls.used_slots << "\n"; - if (cls->first_used_slot == -1) { - cls->first_used_slot = slot; - } + auto unavailable_slots = cls.used_slots; + detail::merge_into(cls.reserved_slots, unavailable_slots); - for (auto b : cls->direct_bases) { - allocate_slot_up(b, slot); - } + ++trace << "unavailable slots: " << unavailable_slots << "\n"; - for (auto d : cls->direct_derived) { - allocate_slot_down(d, slot); - } -} + std::size_t slot = 0; -template -void compiler::allocate_slot_up(class_* cls, std::size_t slot) { + for (; slot < unavailable_slots.size(); ++slot) { + if (!unavailable_slots[slot]) { + break; + } + } - if (cls->mark == class_visit) - return; + ++trace << "first available slot: " << slot << "\n"; - cls->mark = class_visit; + mp.method->slots[mp.param] = slot; + detail::set_bit(cls.used_slots, slot); + detail::set_bit(cls.reserved_slots, slot); - trace << " " << *cls; + { + ++trace << "reserve slots " << cls.used_slots << " in:\n"; + indent _(trace); - assert(slot >= cls->next_slot); - cls->next_slot = slot + 1; + for (auto base : cls.transitive_bases) { + ++trace << *base << "\n"; + detail::merge_into(cls.used_slots, base->reserved_slots); + } + } - if (cls->first_used_slot == -1) { - cls->first_used_slot = slot; - } + { + ++trace << "assign slots " << cls.used_slots << " in:\n"; + indent _(trace); + + for (auto covariant : cls.covariant_classes) { + if (&cls != covariant) { + ++trace << *covariant << "\n"; + detail::merge_into( + cls.used_slots, covariant->used_slots); - for (auto b : cls->direct_bases) { - allocate_slot_up(b, slot); + for (auto base : covariant->transitive_bases) { + ++trace << *base << "\n"; + detail::merge_into( + cls.used_slots, base->reserved_slots); + } + } + } + } + } } - for (auto d : cls->direct_derived) { - allocate_slot_down(d, slot); + for (auto pd : cls.direct_derived) { + assign_lattice_slots(*pd); } } @@ -729,8 +753,8 @@ void compiler::build_dispatch_tables() { << "\n"; indent _(trace); - for (auto compatible_class : vp->compatible_classes) { - ++trace << "specs applicable to " << *compatible_class + for (auto covariant_class : vp->covariant_classes) { + ++trace << "specs applicable to " << *covariant_class << "\n"; bitvec mask; mask.resize(m.specs.size()); @@ -739,9 +763,9 @@ void compiler::build_dispatch_tables() { indent _(trace); for (auto& spec : m.specs) { - if (spec.vp[dim]->compatible_classes.find( - compatible_class) != - spec.vp[dim]->compatible_classes.end()) { + if (spec.vp[dim]->covariant_classes.find( + covariant_class) != + spec.vp[dim]->covariant_classes.end()) { ++trace << type_name(spec.info->type) << "\n"; mask[group_index] = 1; } @@ -749,9 +773,9 @@ void compiler::build_dispatch_tables() { } auto& group = dim_group[mask]; - group.classes.push_back(compatible_class); + group.classes.push_back(covariant_class); group.has_concrete_classes = group.has_concrete_classes || - !compatible_class->is_abstract; + !covariant_class->is_abstract; ++trace << "-> mask: " << mask << "\n"; } @@ -773,24 +797,23 @@ void compiler::build_dispatch_tables() { } for (std::size_t dim = 0; dim < m.arity(); ++dim) { - ++trace << "groups for dim " << dim << ":\n"; indent _(trace); std::size_t group_num = 0; + for (auto& [mask, group] : groups[dim]) { + ++trace << "groups for dim " << dim << ":\n"; + indent _(trace); + ++trace << group_num << " mask " << mask << ":\n"; + for (auto cls : group.classes) { - auto& entry = cls->vtbl[m.slots[dim]]; + indent _(trace); + ++trace << type_name(cls->type_ids[0]) << "\n"; + auto& entry = cls->vtbl[m.slots[dim] - cls->first_slot]; entry.method_index = &m - &methods[0]; entry.vp_index = dim; entry.group_index = group_num; } - if constexpr (trace_enabled) { - ++trace << group_num << " mask " << mask << "\n"; - indent _(trace); - for (auto cls : - range{group.classes.begin(), group.classes.end()}) { - ++trace << type_name(cls->type_ids[0]) << "\n"; - } - } + ++group_num; } } @@ -972,134 +995,105 @@ inline void generic_compiler::accumulate( total.concrete_ambiguous += partial.concrete_ambiguous != 0; } -template -template -void compiler::write_spec_name( - const generic_compiler::method& method, - const generic_compiler::definition* def, Stream& trace) { - - if (def == &method.ambiguous) { - trace << "ambiguous"; - } else if (def == &method.not_implemented) { - trace << "not implemented"; - } else { - Policy::type_name(def->info->type, trace); - } -} - template void compiler::install_gv() { using namespace policy; - for (std::size_t pass = 0; pass != 2; ++pass) { - Policy::dispatch_data.resize(0); + std::size_t dispatch_data_size = std::accumulate( + methods.begin(), methods.end(), 0, + [](auto sum, auto& m) { return sum + m.dispatch_table.size(); }); + dispatch_data_size += std::accumulate( + classes.begin(), classes.end(), 0, + [](auto sum, auto& cls) { return sum + cls.vtbl.size(); }); - if constexpr (trace_enabled) { - if (pass) { - ++trace << "Initializing multi-method dispatch tables at " - << Policy::dispatch_data.data() << "\n"; - } + Policy::dispatch_data.resize(dispatch_data_size); + auto gv_first = Policy::dispatch_data.data(); + auto gv_last = gv_first + Policy::dispatch_data.size(); + auto gv_iter = gv_first; + + ++trace << "Initializing multi-method dispatch tables at " << gv_iter + << "\n"; + + for (auto& m : methods) { + if (m.info->arity() == 1) { + // Uni-methods just need an index in the method table. + m.info->slots_strides_ptr[0] = m.slots[0]; + continue; } - for (auto& m : methods) { - if (m.info->arity() == 1) { - // Uni-methods just need an index in the method table. - m.info->slots_strides_ptr[0] = m.slots[0]; - continue; - } + // multi-methods only - // multi-methods only + auto strides_iter = std::copy( + m.slots.begin(), m.slots.end(), m.info->slots_strides_ptr); + std::copy(m.strides.begin(), m.strides.end(), strides_iter); - auto strides_iter = std::copy( - m.slots.begin(), m.slots.end(), m.info->slots_strides_ptr); - std::copy(m.strides.begin(), m.strides.end(), strides_iter); + if constexpr (trace_enabled) { + ++trace << rflush(4, Policy::dispatch_data.size()) << " " + << " method #" << m.dispatch_table[0]->method_index << " " + << m.info->name << "\n"; + indent _(trace); - if constexpr (trace_enabled) { - if (pass) { - ++trace << rflush(4, Policy::dispatch_data.size()) << " " - << " method #" << m.dispatch_table[0]->method_index - << " " << m.info->name << "\n"; - indent _(trace); - for (auto& entry : m.dispatch_table) { - ++trace << "spec #" << entry->spec_index << " "; - write_spec_name(m, entry, trace); - trace << "\n"; - } - } + for (auto& entry : m.dispatch_table) { + ++trace << "spec #" << entry->spec_index << " " + << spec_name(m, entry) << "\n"; } - - m.gv_dispatch_table = - Policy::dispatch_data.data() + Policy::dispatch_data.size(); - std::transform( - m.dispatch_table.begin(), m.dispatch_table.end(), - std::back_inserter(Policy::dispatch_data), - [](auto spec) { return spec->pf; }); } - if constexpr (trace_enabled) { - if (pass) { - ++trace << "Initializing v-tables at " - << (Policy::dispatch_data.data() + - Policy::dispatch_data.size()) - << "\n"; - } + m.gv_dispatch_table = gv_iter; + assert(gv_iter + m.dispatch_table.size() <= gv_last); + gv_iter = std::transform( + m.dispatch_table.begin(), m.dispatch_table.end(), gv_iter, + [](auto spec) { return spec->pf; }); + } + + ++trace << "Initializing v-tables at " << gv_iter << "\n"; + + for (auto& cls : classes) { + if (cls.first_slot == -1) { + // corner case: no methods for this class + *cls.static_vptr = gv_iter; + continue; } - for (auto& cls : classes) { - if (cls.first_used_slot == -1) { - // corner case: no methods for this class - *cls.static_vptr = - Policy::dispatch_data.data() + Policy::dispatch_data.size(); - } else { - *cls.static_vptr = Policy::dispatch_data.data() + - Policy::dispatch_data.size() - cls.first_used_slot; - } + *cls.static_vptr = gv_iter - cls.first_slot; - if constexpr (trace_enabled) { - if (pass) { - ++trace << rflush(4, Policy::dispatch_data.size()) << " " - << *cls.static_vptr << " vtbl for " << cls - << " slots " << cls.first_used_slot << "-" - << cls.vtbl.size() << "\n"; - indent _(trace); + ++trace << rflush(4, gv_iter - gv_first) << " " << gv_iter + << " vtbl for " << cls << " slots " << cls.first_slot << "-" + << (cls.first_slot + cls.vtbl.size() - 1) << "\n"; + indent _(trace); - for (auto& entry : cls.vtbl) { - ++trace << "method #" << entry.method_index << " "; - auto& method = methods[entry.method_index]; - - if (method.arity() == 1) { - auto spec = - method.dispatch_table[entry.group_index]; - trace << "spec #" << spec->spec_index << "\n"; - indent _(trace); - Policy::type_name( - method.info->method_type, ++trace); - trace << "\n"; - write_spec_name(method, spec, ++trace); - } else { - trace << "vp #" << entry.vp_index << " group #" - << entry.group_index << "\n"; - indent _(trace); - Policy::type_name( - method.info->method_type, ++trace); - } + for (auto& entry : cls.vtbl) { + ++trace << "method #" << entry.method_index << " "; + auto& method = methods[entry.method_index]; - trace << "\n"; - } + if (method.arity() == 1) { + auto spec = method.dispatch_table[entry.group_index]; + trace << "spec #" << spec->spec_index << "\n"; + indent _(trace); + ++trace << type_name(method.info->method_type) << "\n"; + ++trace << spec_name(method, spec); + assert(gv_iter + 1 <= gv_last); + *gv_iter++ = spec->pf; + } else { + trace << "vp #" << entry.vp_index << " group #" + << entry.group_index << "\n"; + indent _(trace); + ++trace << type_name(method.info->method_type); + assert(gv_iter + 1 <= gv_last); + + if (entry.vp_index == 0) { + *gv_iter++ = std::uintptr_t( + method.gv_dispatch_table + entry.group_index); + } else { + *gv_iter++ = entry.group_index; } } - if (cls.first_used_slot != -1) { - std::transform( - cls.vtbl.begin() + cls.first_used_slot, cls.vtbl.end(), - std::back_inserter(Policy::dispatch_data), - [](auto entry) { return entry.group_index; }); - } + trace << "\n"; } } - ++trace << rflush(4, Policy::dispatch_data.size()) << " " - << Policy::dispatch_data.data() + Policy::dispatch_data.size() + ++trace << rflush(4, Policy::dispatch_data.size()) << " " << gv_iter << " end\n"; if constexpr (has_facet) { @@ -1107,42 +1101,6 @@ void compiler::install_gv() { } } -template -void compiler::optimize() { - ++trace << "Optimizing\n"; - - for (auto& m : methods) { - ++trace << " " << m.info->name << "\n"; - indent _(trace); - auto slot = m.slots[0]; - - if (m.arity() == 1) { - for (auto cls : m.vp[0]->compatible_classes) { - auto spec = m.dispatch_table[(*cls->static_vptr)[slot]]; - if constexpr (trace_enabled) { - ++trace << *cls << " vtbl[" << slot - << "] = " << spec->method_index << "/" - << spec->spec_index << " function " - << (void*)spec->pf << "\n"; - } - (*cls->static_vptr)[slot] = spec->pf; - } - } else { - for (auto cls : m.vp[0]->compatible_classes) { - auto pw = m.gv_dispatch_table + (*cls->static_vptr)[slot]; - - if constexpr (trace_enabled) { - ++trace << *cls << " vtbl[" << slot << "] = gv+" - << (pw - Policy::dispatch_data.data()) << "\n"; - } - - (*cls->static_vptr)[slot] = - reinterpret_cast(pw); - } - } - } -} - template std::vector compiler::best(std::vector& candidates) { @@ -1179,12 +1137,12 @@ bool compiler::is_more_specific( for (; a_iter != a_last; ++a_iter, ++b_iter) { if (*a_iter != *b_iter) { - if ((*b_iter)->compatible_classes.find(*a_iter) != - (*b_iter)->compatible_classes.end()) { + if ((*b_iter)->covariant_classes.find(*a_iter) != + (*b_iter)->covariant_classes.end()) { result = true; } else if ( - (*a_iter)->compatible_classes.find(*b_iter) != - (*a_iter)->compatible_classes.end()) { + (*a_iter)->covariant_classes.find(*b_iter) != + (*a_iter)->covariant_classes.end()) { return false; } } @@ -1201,8 +1159,8 @@ bool compiler::is_base(const definition* a, const definition* b) { for (; a_iter != a_last; ++a_iter, ++b_iter) { if (*a_iter != *b_iter) { - if ((*a_iter)->compatible_classes.find(*b_iter) == - (*a_iter)->compatible_classes.end()) { + if ((*a_iter)->covariant_classes.find(*b_iter) == + (*a_iter)->covariant_classes.end()) { return false; } else { result = true; diff --git a/include/yorel/yomm2/generator.hpp b/include/yorel/yomm2/generator.hpp index ed99f3ca..5f2a8376 100644 --- a/include/yorel/yomm2/generator.hpp +++ b/include/yorel/yomm2/generator.hpp @@ -24,6 +24,7 @@ namespace yomm2 { class generator { public: +#ifndef _MSC_VER generator& add_forward_declaration(const std::type_info& type); template generator& add_forward_declaration(); @@ -31,6 +32,7 @@ class generator { template generator& add_forward_declarations(); const generator& write_forward_declarations(std::ostream& os) const; +#endif template const generator& write_static_offsets(std::ostream& os) const; template @@ -82,6 +84,8 @@ inline std::unordered_set generator::keywords = { }; // clang-format on +#ifndef _MSC_VER + inline generator& generator::add_forward_declaration(std::string_view type) { using namespace detail; @@ -223,6 +227,8 @@ const generator& generator::write_static_offsets(std::ostream& os) const { return *this; } +#endif + void generator::write_static_offsets( const detail::method_info& method, std::ostream& os) const { using namespace detail; @@ -307,8 +313,7 @@ void generator::encode_dispatch_data( for (auto& cls : compiler.classes) { ++encode_vtbl_size; // for first slot index - for (auto& entry : - range(cls.vtbl.begin() + cls.first_used_slot, cls.vtbl.end())) { + for (auto& entry : cls.vtbl) { if (entry.vp_index != 0) { // It's a multi-method, and not the first virtual parameter. // Encode the index, it will be decoded as is. @@ -320,7 +325,7 @@ void generator::encode_dispatch_data( } } - decode_vtbl_size += cls.vtbl.size() - cls.first_used_slot; + decode_vtbl_size += cls.vtbl.size() - cls.first_slot; } // ------------------------------------------------------------------------- @@ -400,7 +405,7 @@ void generator::encode_dispatch_data( ->name()) << "\n"; - os << indent << cls.first_used_slot << ", // first used slot\n"; + os << indent << cls.first_slot << ", // first used slot\n"; for (auto& entry : cls.vtbl) { os << indent; diff --git a/include/yorel/yomm2/templates.hpp b/include/yorel/yomm2/templates.hpp index 5f2983fb..1ca05a27 100644 --- a/include/yorel/yomm2/templates.hpp +++ b/include/yorel/yomm2/templates.hpp @@ -86,7 +86,7 @@ template std::false_type has_method_aux(...); template -constexpr bool has_method = decltype(detail::has_method_aux(nullptr))::value; +constexpr bool has_method = decltype(has_method_aux(nullptr))::value; template typename Definition> struct use_definition { diff --git a/tests/chain.cpp b/tests/chain.cpp index e5103b17..6053251e 100644 --- a/tests/chain.cpp +++ b/tests/chain.cpp @@ -10,95 +10,100 @@ using namespace yorel::yomm2::detail; -struct value : chain::link { - int id; -}; - -// std::ostream& -// operator<<(std::ostream& os, static_chain::iterator iter) { -// return os; // << &*iter; -// } +struct value : static_chain::static_link {}; -using test_iter = chain::iterator; +using test_iter = static_chain::iterator; BOOST_TEST_DONT_PRINT_LOG_VALUE(test_iter); BOOST_AUTO_TEST_CASE(test_chain) { - chain l; - value a, b, c, d; + static static_chain l; + static value a, b, c, d; BOOST_TEST_REQUIRE(l.begin() == l.end()); + BOOST_TEST_REQUIRE(l.empty()); - l.push_front(a); + l.push_back(a); + // a BOOST_TEST_REQUIRE(&*l.begin() == &a); - BOOST_TEST_REQUIRE(&l.begin()->id == &a.id); + BOOST_TEST_REQUIRE(!l.empty()); BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 1); static_chain::iterator iter; - l.push_front(b); + l.push_back(b); + // a b BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 2); - // b a // test iterator post-increment iter = l.begin(); - BOOST_TEST_REQUIRE(&iter->id == &b.id); - BOOST_TEST_REQUIRE(&iter++->id == &b.id); - BOOST_TEST_REQUIRE(&iter++->id == &a.id); - BOOST_TEST_REQUIRE(iter == l.end()); + BOOST_TEST_REQUIRE(iter != l.end()); + BOOST_TEST(&*iter++ == &a); + BOOST_TEST_REQUIRE(iter != l.end()); + BOOST_TEST(&*iter++ == &b); + BOOST_TEST(iter == l.end()); - // b a // test iterator pre-increment iter = l.begin(); ++iter; - BOOST_TEST_REQUIRE(&iter->id == &a.id); + BOOST_TEST_REQUIRE(&*iter == &b); - l.push_front(c); + l.push_back(c); + // a b c BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 3); - // c b a - // test remove from front, the commonest case + + // test remove from back, the most common case l.remove(c); + // a b + BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 2); iter = l.begin(); - BOOST_TEST_REQUIRE(&iter++->id == &b.id); - BOOST_TEST_REQUIRE(&iter++->id == &a.id); + BOOST_TEST_REQUIRE(&*iter++ == &a); + BOOST_TEST_REQUIRE(&*iter++ == &b); + + l.push_back(c); + // a b c + BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 3); - l.push_front(c); - l.push_front(d); + l.push_back(d); + // a b c d + BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 4); - // d c b a // test remove from front - l.remove(d); - iter = l.begin(); + l.remove(a); + // b c d + BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 3); - BOOST_TEST_REQUIRE(&iter++->id == &c.id); - BOOST_TEST_REQUIRE(&iter++->id == &b.id); - BOOST_TEST_REQUIRE(&iter++->id == &a.id); + iter = l.begin(); + BOOST_TEST_REQUIRE(&*iter++ == &b); + BOOST_TEST_REQUIRE(&*iter++ == &c); + BOOST_TEST_REQUIRE(&*iter++ == &d); - // c b a // test remove from middle l.remove(b); - iter = l.begin(); + // c d + BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 2); - BOOST_TEST_REQUIRE(&iter++->id == &c.id); - BOOST_TEST_REQUIRE(&iter++->id == &a.id); + iter = l.begin(); + BOOST_TEST_REQUIRE(&*iter++ == &c); + BOOST_TEST_REQUIRE(&*iter++ == &d); - // c a // test remove from end - l.remove(a); + l.remove(c); + // d + iter = l.begin(); BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 1); - BOOST_TEST_REQUIRE(&iter++->id == &c.id); + BOOST_TEST_REQUIRE(&*iter++ == &d); - // a // test remove from end, one item left - l.remove(c); + l.remove(d); BOOST_TEST_REQUIRE(std::distance(l.begin(), l.end()) == 0); } struct static_value : static_chain::static_link { explicit static_value(static_chain& reg) { - reg.push_front(*this); + reg.push_back(*this); } }; @@ -117,9 +122,9 @@ static_chain reg; BOOST_AUTO_TEST_CASE(test_static_link_link_chain) { auto iter = reg.begin(), last = reg.end(); BOOST_TEST_REQUIRE(iter != last); - BOOST_TEST_REQUIRE(&*iter == &b); - BOOST_TEST_REQUIRE(++iter != last); BOOST_TEST_REQUIRE(&*iter == &a); + BOOST_TEST_REQUIRE(++iter != last); + BOOST_TEST_REQUIRE(&*iter == &b); BOOST_TEST_REQUIRE(++iter == last); } } // namespace link_link_chain @@ -133,9 +138,9 @@ static_value b(reg); BOOST_AUTO_TEST_CASE(test_static_link_chain_link) { auto iter = reg.begin(), last = reg.end(); BOOST_TEST_REQUIRE(iter != last); - BOOST_TEST_REQUIRE(&*iter == &b); - BOOST_TEST_REQUIRE(++iter != last); BOOST_TEST_REQUIRE(&*iter == &a); + BOOST_TEST_REQUIRE(++iter != last); + BOOST_TEST_REQUIRE(&*iter == &b); BOOST_TEST_REQUIRE(++iter == last); } } // namespace link_chain_link @@ -148,9 +153,9 @@ static_value b(reg); BOOST_AUTO_TEST_CASE(test_static_chain_link_link) { auto iter = reg.begin(), last = reg.end(); BOOST_TEST_REQUIRE(iter != last); - BOOST_TEST_REQUIRE(&*iter == &b); - BOOST_TEST_REQUIRE(++iter != last); BOOST_TEST_REQUIRE(&*iter == &a); + BOOST_TEST_REQUIRE(++iter != last); + BOOST_TEST_REQUIRE(&*iter == &b); BOOST_TEST_REQUIRE(++iter == last); } } // namespace chain_link_link diff --git a/tests/compiler.cpp b/tests/compiler.cpp index 89e0bd02..fc50a01c 100644 --- a/tests/compiler.cpp +++ b/tests/compiler.cpp @@ -39,15 +39,6 @@ auto str(const Container& container) { return os.str(); } -template -auto str(T... args) { - std::ostringstream os; - os << "{"; - const char* sep = ""; - ((os << sep, os << args, sep = ", "), ...); - return os.str(); -} - template auto sstr(Ts... args) { std::vector vec{args...}; @@ -60,537 +51,18 @@ auto sstr(const std::unordered_set& container) { return sstr(std::vector(container.begin(), container.end())); } -template -bool contains(const C& coll, const T& value) { - return std::find(coll.begin(), coll.end(), value) != coll.end(); -} - -template -std::ostream& operator<<(std::ostream& os, const std::vector& vec) { - os << "{ "; - const char* sep = ""; - for (auto& val : vec) { - os << sep << val; - sep = ", "; - } - os << " }"; - return os; -} - template auto get_class(const Compiler& comp) { return comp.class_map.at(typeid(T)); } -namespace ns_use_classes { - -struct Animal { - virtual ~Animal() { - } -}; - -struct Herbivore : virtual Animal {}; -struct Carnivore : virtual Animal {}; -struct Cow : Herbivore {}; -struct Wolf : Carnivore {}; -struct Human : Carnivore, Herbivore {}; - -using whole_hierarchy = test_policy_<__COUNTER__>; -using incremental = test_policy_<__COUNTER__>; - -use_classes - YOMM2_GENSYM; - -YOMM2_STATIC(use_classes); -YOMM2_STATIC(use_classes); -YOMM2_STATIC(use_classes); - -using policies = std::tuple; - -BOOST_AUTO_TEST_CASE_TEMPLATE(test_use_classes, Key, policies) { - compiler comp; - comp.compile(); - comp.install_global_tables(); - - auto animal = get_class(comp); - auto herbivore = get_class(comp); - auto carnivore = get_class(comp); - auto cow = get_class(comp); - auto wolf = get_class(comp); - auto human = get_class(comp); - - BOOST_TEST(animal->direct_bases.empty()); - BOOST_TEST(sstr(animal->direct_derived) == sstr(herbivore, carnivore)); - BOOST_TEST(sstr(herbivore->direct_bases) == sstr(animal)); - BOOST_TEST(sstr(herbivore->direct_derived) == sstr(cow, human)); - BOOST_TEST(sstr(carnivore->direct_bases) == sstr(animal)); - BOOST_TEST(sstr(carnivore->direct_derived) == sstr(wolf, human)); - BOOST_TEST(sstr(cow->direct_bases) == sstr(herbivore)); - BOOST_TEST(cow->direct_derived.empty()); - BOOST_TEST(sstr(wolf->direct_bases) == sstr(carnivore)); - BOOST_TEST(wolf->direct_derived.empty()); - BOOST_TEST(sstr(human->direct_bases) == sstr(carnivore, herbivore)); - BOOST_TEST(human->direct_derived.empty()); -} -} // namespace ns_use_classes - -namespace rolex { - -struct Role { - virtual ~Role() { - } -}; - -struct Employee : Role {}; -struct Manager : Employee {}; -struct Founder : Role {}; - -struct Expense { - virtual ~Expense() { - } -}; - -struct Public : Expense {}; -struct Bus : Public {}; -struct Metro : Public {}; -struct Taxi : Expense {}; -struct Jet : Expense {}; - -using test_policy = test_policy_<__COUNTER__>; -// any type from this namespace would work. - -use_classes - YOMM2_GENSYM; - -YOMM2_STATIC(use_classes); - -YOMM2_DECLARE(double, pay, (virtual_), test_policy); - -YOMM2_DECLARE( - bool, approve, (virtual_, virtual_, double), - test_policy); - -YOMM2_DEFINE(double, pay, (const Employee&)) { - return 3000; -} - -YOMM2_DEFINE(double, pay, (const Manager& exec)) { - return next(exec) + 2000; -} - -YOMM2_DEFINE(bool, approve, (const Role& r, const Expense& e, double amount)) { - return false; -} - -YOMM2_DEFINE( - bool, approve, (const Employee& r, const Public& e, double amount)) { - return true; -} - -YOMM2_DEFINE(bool, approve, (const Manager& r, const Taxi& e, double amount)) { - return true; -} - -YOMM2_DEFINE( - bool, approve, (const Founder& r, const Expense& e, double amount)) { - return true; -} - -BOOST_AUTO_TEST_CASE(runtime_test) { - - compiler comp; - - comp.augment_classes(); - - auto role = get_class(comp); - auto employee = get_class(comp); - auto manager = get_class(comp); - auto founder = get_class(comp); - auto expense = get_class(comp); - auto public_ = get_class(comp); - auto bus = get_class(comp); - auto metro = get_class(comp); - auto taxi = get_class(comp); - auto jet = get_class(comp); - - BOOST_TEST(sstr(role->direct_bases) == empty); - BOOST_TEST(sstr(employee->direct_bases) == sstr(role)); - - { - std::vector expected = {employee}; - BOOST_TEST(manager->direct_bases == expected); - } - - { - std::vector expected = {role}; - BOOST_TEST(founder->direct_bases == expected); - } - - { - std::vector expected = {expense}; - BOOST_TEST(public_->direct_bases == expected); - } - - { - std::vector expected = {public_}; - BOOST_TEST(bus->direct_bases == expected); - } - - { - std::vector expected = {public_}; - BOOST_TEST(metro->direct_bases == expected); - } - - { - std::vector expected = {expense}; - BOOST_TEST(taxi->direct_bases == expected); - } - - { - std::vector expected = {expense}; - BOOST_TEST(jet->direct_bases == expected); - } - - { - std::vector expected = {employee, founder}; - BOOST_TEST(sstr(role->direct_derived) == sstr(expected)); - } - - { - std::vector expected = {manager}; - BOOST_TEST(sstr(employee->direct_derived) == sstr(expected)); - } - - { - std::vector expected = {public_, taxi, jet}; - BOOST_TEST(sstr(expense->direct_derived) == sstr(expected)); - } - - { - std::vector expected = {bus, metro}; - BOOST_TEST(sstr(public_->direct_derived) == sstr(expected)); - } - - BOOST_TEST(bus->direct_derived.size() == 0); - BOOST_TEST(metro->direct_derived.size() == 0); - BOOST_TEST(taxi->direct_derived.size() == 0); - BOOST_TEST(jet->direct_derived.size() == 0); - - BOOST_TEST( - sstr(role->compatible_classes) == - sstr(role, employee, founder, manager)); - BOOST_TEST(sstr(founder->compatible_classes) == sstr(founder)); - BOOST_TEST( - sstr(expense->compatible_classes) == - sstr(expense, public_, taxi, jet, bus, metro)); - BOOST_TEST(sstr(public_->compatible_classes) == sstr(public_, bus, metro)); - - comp.augment_methods(); - - BOOST_TEST_REQUIRE(comp.methods.size() == 2); - auto method_iter = comp.methods.begin(); - cc_method& approve_method = *method_iter++; - BOOST_TEST_REQUIRE(approve_method.vp.size() == 2); - cc_method& pay_method = *method_iter++; - BOOST_TEST_REQUIRE(pay_method.vp.size() == 1); - - { - std::vector expected = {employee}; - BOOST_TEST_INFO("result = " + sstr(pay_method.vp)); - BOOST_TEST_INFO("expected = " + sstr(expected)); - BOOST_TEST(pay_method.vp == expected); - } - - { - std::vector expected = {role, expense}; - BOOST_TEST_INFO("result = " << sstr(approve_method.vp)); - BOOST_TEST_INFO("expected = " << sstr(expected)); - BOOST_TEST(approve_method.vp == expected); - } - - { - auto c_iter = test_policy::methods.begin(); - auto r_iter = comp.methods.begin(); - - for (int i = 0; i < comp.methods.size(); ++i) { - BOOST_TEST_INFO("i = " << i); - auto& c_meth = *c_iter++; - auto& r_meth = *r_iter++; - BOOST_TEST_REQUIRE(r_meth.specs.size() == c_meth.specs.size()); - - auto c_spec_iter = c_meth.specs.begin(); - auto r_spec_iter = r_meth.specs.begin(); - - for (int j = 0; j < r_meth.specs.size(); ++j) { - BOOST_TEST_INFO("i = " << i); - BOOST_TEST_INFO("j = " << j); - auto& c_spec = *c_spec_iter++; - auto& r_spec = *r_spec_iter++; - BOOST_TEST_REQUIRE( - r_spec.vp.size() == c_spec.vp_end - c_spec.vp_begin); - } - } - } - - BOOST_TEST_REQUIRE(role->used_by_vp.size() == 1); - BOOST_TEST(role->used_by_vp[0].method == &approve_method); - BOOST_TEST(role->used_by_vp[0].param == 0); - - BOOST_TEST_REQUIRE(employee->used_by_vp.size() == 1); - BOOST_TEST(employee->used_by_vp[0].method == &pay_method); - BOOST_TEST(employee->used_by_vp[0].param == 0); - - BOOST_TEST_REQUIRE(expense->used_by_vp.size() == 1); - BOOST_TEST(expense->used_by_vp[0].method == &approve_method); - BOOST_TEST(expense->used_by_vp[0].param == 1); - - comp.allocate_slots(); - - { - const std::vector expected = {1}; - BOOST_TEST(expected == pay_method.slots); - } - - { - const std::vector expected = {0, 0}; - BOOST_TEST(expected == approve_method.slots); - } - - BOOST_TEST_REQUIRE(role->vtbl.size() == 1); - BOOST_TEST_REQUIRE(employee->vtbl.size() == 2); - BOOST_TEST_REQUIRE(manager->vtbl.size() == 2); - BOOST_TEST_REQUIRE(founder->vtbl.size() == 1); - - BOOST_TEST_REQUIRE(expense->vtbl.size() == 1); - BOOST_TEST_REQUIRE(public_->vtbl.size() == 1); - BOOST_TEST_REQUIRE(bus->vtbl.size() == 1); - BOOST_TEST_REQUIRE(metro->vtbl.size() == 1); - BOOST_TEST_REQUIRE(taxi->vtbl.size() == 1); - BOOST_TEST_REQUIRE(jet->vtbl.size() == 1); - - auto pay_method_iter = pay_method.specs.begin(); - auto pay_Manager = pay_method_iter++; - auto pay_Employee = pay_method_iter++; - - auto spec_iter = approve_method.specs.begin(); - auto approve_Founder_Expense = spec_iter++; - auto approve_Manager_Taxi = spec_iter++; - auto approve_Employee_public = spec_iter++; - auto approve_Role_Expense = spec_iter++; - - { - BOOST_TEST(compiler::is_more_specific( - &*approve_Founder_Expense, &*approve_Role_Expense)); - BOOST_TEST(compiler::is_more_specific( - &*approve_Manager_Taxi, &*approve_Role_Expense)); - BOOST_TEST(!compiler::is_more_specific( - &*approve_Role_Expense, &*approve_Role_Expense)); - - { - std::vector expected = {&*approve_Manager_Taxi}; - std::vector specs = { - &*approve_Role_Expense, &*approve_Manager_Taxi}; - BOOST_TEST(expected == compiler::best(specs)); - } - } - - { - BOOST_TEST(compiler::is_base( - &*approve_Role_Expense, &*approve_Founder_Expense)); - BOOST_TEST(!compiler::is_base( - &*approve_Role_Expense, &*approve_Role_Expense)); - } - - comp.build_dispatch_tables(); - - { - BOOST_TEST_REQUIRE(pay_method.dispatch_table.size() == 2); - BOOST_TEST(pay_method.dispatch_table[0]->pf == pay_Employee->pf); - BOOST_TEST(pay_method.dispatch_table[1]->pf == pay_Manager->pf); - } - - - BOOST_TEST_REQUIRE(pay_Employee->info->next != nullptr); - BOOST_TEST_REQUIRE(pay_Manager->info->next != nullptr); - BOOST_TEST(*pay_Manager->info->next == pay_Employee->info->pf); - - comp.install_gv(); - - { - // pay - BOOST_TEST_REQUIRE( - test_policy::dispatch_data.size() == - +12 // approve: 3 slots and 12 cells for dispatch table - + 12); // 3 vtbl of 2 cells for Roles + 6 vtbl of 1 cells for - // Expenses - BOOST_TEST_REQUIRE( - test_policy::vptrs.size() <= - (1 - << (std::numeric_limits::digits - - test_policy::hash_shift))); -#ifndef NDEBUG - BOOST_TEST_REQUIRE( - test_policy::control.size() == test_policy::vptrs.size()); -#endif - - auto gv_iter = test_policy::dispatch_data.data(); - // no slots nor fun* for 1-method - - // approve - // 12 fun* - auto approve_dispatch_table = gv_iter; - BOOST_TEST(std::equal( - approve_method.dispatch_table.begin(), - approve_method.dispatch_table.end(), gv_iter, - [](auto a, auto b) { return a->pf == b; })); - gv_iter += approve_method.dispatch_table.size(); - - // auto opt_iter = gv_iter; - - // // Role - // BOOST_TEST(gv_iter++->i == 0); // approve/0 - // // Employee - // BOOST_TEST(gv_iter++->i == 1); // approve/0 - // BOOST_TEST(gv_iter++->i == 0); // pay - // // Manager - // BOOST_TEST(gv_iter++->i == 2); // approve/0 - // BOOST_TEST(gv_iter++->i == 1); // pay - // // owner - // BOOST_TEST(gv_iter++->i == 3); // approve/0 - // // Expense - // BOOST_TEST(gv_iter++->i == 0); // approve/1 - // // Public - // BOOST_TEST(gv_iter++->i == 1); // approve/1 - // // Bus - // BOOST_TEST(gv_iter++->i == 1); // approve/1 - // // Metro - // BOOST_TEST(gv_iter++->i == 1); // approve/1 - // // Taxi - // BOOST_TEST(gv_iter++->i == 2); // approve/1 - // // Plane - // BOOST_TEST(gv_iter++->i == 0); // approve/1 - - comp.optimize(); - - // // Role - // BOOST_TEST(opt_iter++->pw == 0 + approve_dispatch_table); // - // approve/0 - // // Employee - // BOOST_TEST(opt_iter++->pw == 1 + approve_dispatch_table); // - // approve/0 BOOST_TEST(opt_iter++->pf == pay_Employee->info->pf); // - // pay - // // Manager - // BOOST_TEST(opt_iter++->pw == 2 + approve_dispatch_table); // - // approve/0 BOOST_TEST(opt_iter++->pf == pay_Manager->info->pf); // pay - // // owner - // BOOST_TEST(opt_iter++->pw == 3 + approve_dispatch_table); // - // approve/0 - // // Expense - // BOOST_TEST(opt_iter++->i == 0); // approve/1 - // // Public - // BOOST_TEST(opt_iter++->i == 1); // approve/1 - // // Bus - // BOOST_TEST(opt_iter++->i == 1); // approve/1 - // // Metro - // BOOST_TEST(opt_iter++->i == 1); // approve/1 - // // Taxi - // BOOST_TEST(opt_iter++->i == 2); // approve/1 - // // Plane - // BOOST_TEST(opt_iter++->i == 0); // approve/1 - - // BOOST_TEST(vptr(test_policy::context, &typeid(Role)) == role->vptr); - // BOOST_TEST( - // vptr(test_policy::context, &typeid(Employee)) == employee->vptr); - // BOOST_TEST( - // vptr(test_policy::context, &typeid(Manager)) == manager->vptr); - // BOOST_TEST( - // vptr(test_policy::context, &typeid(Founder)) == founder->vptr); - - // BOOST_TEST( - // vptr(test_policy::context, &typeid(Expense)) == expense->vptr); - // BOOST_TEST( - // vptr(test_policy::context, &typeid(Public)) == public_->vptr); - // BOOST_TEST(vptr(test_policy::context, &typeid(Bus)) == bus->vptr); - // BOOST_TEST(vptr(test_policy::context, &typeid(Metro)) == - // metro->vptr); BOOST_TEST(vptr(test_policy::context, &typeid(Taxi)) == - // taxi->vptr); BOOST_TEST(vptr(test_policy::context, &typeid(Jet)) == - // jet->vptr); - - { - const Role& role = Role(); - const Employee& employee = Employee(); - const Employee& manager = Manager(); - const Role& founder = Founder(); - - const Expense& expense = Expense(); - const Expense& public_transport = Public(); - const Expense& bus = Bus(); - const Expense& metro = Metro(); - const Expense& taxi = Taxi(); - const Expense& jet = Jet(); - - const auto& pay_method = - decltype(yOMM2_SELECTOR(pay)(Employee()))::fn; - BOOST_TEST(pay_method.arity == 1); - BOOST_TEST(pay_method.resolve(employee) == pay_Employee->info->pf); - BOOST_TEST(&typeid(manager) == &typeid(Manager)); - BOOST_TEST(pay_method.resolve(manager) == pay_Manager->info->pf); - - using approve_method = - decltype(yOMM2_SELECTOR(approve)(Role(), Expense(), 0.)); - BOOST_TEST(approve_method::fn.arity == 2); - - BOOST_TEST( - (approve_method::fn.resolve(role, expense, 0.)) == - approve_Role_Expense->info->pf); - - { - std::vector Roles = { - &role, &employee, &manager, &founder}; - - std::vector Expenses = { - &expense, &public_transport, &bus, &metro, &taxi, &jet}; - - int i = 0; - - for (auto r : Roles) { - int j = 0; - for (auto e : Expenses) { - BOOST_TEST_INFO("i = " << i << " j = " << j); - auto expected = typeid(*r) == typeid(Founder) - ? approve_Founder_Expense - : typeid(*r) == typeid(Manager) - ? (typeid(*e) == typeid(Taxi) ? approve_Manager_Taxi - : dynamic_cast(e) - ? approve_Employee_public - : approve_Role_Expense) - : typeid(*r) == typeid(Employee) && - dynamic_cast(e) - ? approve_Employee_public - : approve_Role_Expense; - BOOST_TEST( - (approve_method::fn.resolve(*r, *e, 0.)) == - expected->info->pf); - ++j; - } - ++i; - } - } - - BOOST_TEST(pay(employee) == 3000); - BOOST_TEST(pay(manager) == 5000); - } - } -} -} // namespace rolex - -namespace multiple_inheritance { - // A B // \ / \ // AB D // | | // C E +// \ / +// F struct A { virtual ~A() { @@ -610,15 +82,18 @@ struct D : B {}; struct E : D {}; -using test_policy = test_policy_<__COUNTER__>; +struct F : C, E {}; + +// ============================================================================ +// Test use_classes. -YOMM2_STATIC(use_classes); +BOOST_AUTO_TEST_CASE(test_use_classes) { + using test_policy = test_policy_<__COUNTER__>; + YOMM2_STATIC(use_classes); -BOOST_AUTO_TEST_CASE(test_use_classes_mi) { std::vector actual, expected; - compiler comp; - comp.augment_classes(); + auto comp = update(); auto a = get_class(comp); auto b = get_class(comp); @@ -631,104 +106,251 @@ BOOST_AUTO_TEST_CASE(test_use_classes_mi) { // A BOOST_REQUIRE_EQUAL(sstr(a->direct_bases), empty); BOOST_REQUIRE_EQUAL(sstr(a->direct_derived), sstr(ab)); - BOOST_REQUIRE_EQUAL(sstr(a->compatible_classes), sstr(a, ab, c)); + BOOST_REQUIRE_EQUAL(sstr(a->covariant_classes), sstr(a, ab, c)); // ----------------------------------------------------------------------- // B BOOST_REQUIRE_EQUAL(sstr(b->direct_bases), empty); BOOST_REQUIRE_EQUAL(sstr(b->direct_derived), sstr(ab, d)); - BOOST_REQUIRE_EQUAL(sstr(b->compatible_classes), sstr(b, ab, c, d, e)); + BOOST_REQUIRE_EQUAL(sstr(b->covariant_classes), sstr(b, ab, c, d, e)); // ----------------------------------------------------------------------- // AB BOOST_REQUIRE_EQUAL(sstr(ab->direct_bases), sstr(a, b)); BOOST_REQUIRE_EQUAL(sstr(ab->direct_derived), sstr(c)); - BOOST_REQUIRE_EQUAL(sstr(ab->compatible_classes), sstr(ab, c)); + BOOST_REQUIRE_EQUAL(sstr(ab->covariant_classes), sstr(ab, c)); // ----------------------------------------------------------------------- // C BOOST_REQUIRE_EQUAL(sstr(c->direct_bases), sstr(ab)); BOOST_REQUIRE_EQUAL(sstr(c->direct_derived), empty); - BOOST_REQUIRE_EQUAL(sstr(c->compatible_classes), sstr(c)); + BOOST_REQUIRE_EQUAL(sstr(c->covariant_classes), sstr(c)); // ----------------------------------------------------------------------- // D BOOST_REQUIRE_EQUAL(sstr(d->direct_bases), sstr(b)); BOOST_REQUIRE_EQUAL(sstr(d->direct_derived), sstr(e)); - BOOST_REQUIRE_EQUAL(sstr(d->compatible_classes), sstr(d, e)); + BOOST_REQUIRE_EQUAL(sstr(d->covariant_classes), sstr(d, e)); // ----------------------------------------------------------------------- // E BOOST_REQUIRE_EQUAL(sstr(e->direct_bases), sstr(d)); BOOST_REQUIRE_EQUAL(sstr(e->direct_derived), empty); - BOOST_REQUIRE_EQUAL(sstr(e->compatible_classes), sstr(e)); + BOOST_REQUIRE_EQUAL(sstr(e->covariant_classes), sstr(e)); } -struct key; -auto& m_a = method), test_policy>::fn; -auto& m_b = method), test_policy>::fn; -auto& m_ab = method, virtual_), test_policy>::fn; -auto& m_c = method), test_policy>::fn; -auto& m_d = method), test_policy>::fn; - -BOOST_AUTO_TEST_CASE(test_allocate_slots_mi) { - compiler comp; - comp.augment_classes(); - comp.augment_methods(); - comp.allocate_slots(); - - auto m_iter = comp.methods.begin(); - auto m_d = m_iter++; - auto m_c = m_iter++; - auto m_ab = m_iter++; - auto m_b = m_iter++; - auto m_a = m_iter++; - - - // lattice: - // A B - // \ / \ - // AB D - // | | - // C E - - // 1-methods use one slot: - BOOST_TEST(m_a->slots.size() == 1); - BOOST_TEST(m_b->slots.size() == 1); - BOOST_TEST(m_c->slots.size() == 1); - BOOST_TEST(m_d->slots.size() == 1); - - // 2-method uses two slots - BOOST_TEST(m_ab->slots.size() == 2); - // two *different* slots - BOOST_TEST(m_ab->slots[0] != m_ab->slots[1]); - - // m_c and m_d use the same slot - BOOST_TEST(str(m_c->slots) == str(m_d->slots)); - - // check that no two methods methods (except m_d) use a same slot - - decltype(m_a) methods[] = {m_a, m_b, m_ab, m_c}; - - for (auto m1 : methods) { - std::vector in_use(5); // in total methods use 5 slots - - for (auto s1 : m1->slots) { - BOOST_REQUIRE(s1 <= 4); - in_use[s1] = true; - } - - for (auto m2 : methods) { - if (m1 == m2) { - continue; - } +/// ============================================================================ +// Test assign_slots. - for (auto s2 : m2->slots) { - BOOST_REQUIRE(s2 <= 4); - BOOST_TEST(!in_use[s2]); - } +template +const auto& get_method(const Compiler& comp, const method_info& info) { + for (const auto& m : comp.methods) { + if (m.info == &info) { + return m; } } + + BOOST_FAIL("method not found"); + + return comp.methods.front(); +} + +template +struct M; + +#define ADD_METHOD(CLASS) \ + auto& BOOST_PP_CAT(m_, CLASS) = \ + method), test_policy>::fn; + +#define ADD_METHOD_N(CLASS, N) \ + auto& BOOST_PP_CAT(BOOST_PP_CAT(m_, CLASS), N) = \ + method, void(virtual_), test_policy>::fn; + +BOOST_AUTO_TEST_CASE(test_assign_slots_a_b1_c) { + using test_policy = test_policy_<__COUNTER__>; + + // A + // / \ + // B1 C + + struct A {}; + struct B : A {}; + struct C : A {}; + + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + ADD_METHOD(B); + auto comp = update(); + + BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1); + BOOST_TEST(get_method(comp, m_B).slots[0] == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 0); +} + +BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_c1) { + using test_policy = test_policy_<__COUNTER__>; + + // A1 + // / \ + // B1 C1 + + struct A {}; + struct B : A {}; + struct C : A {}; + + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + ADD_METHOD(A); + ADD_METHOD(B); + ADD_METHOD(C); + auto comp = update(); + + BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1); + BOOST_TEST(get_method(comp, m_A).slots[0] == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); + + BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1); + BOOST_TEST(get_method(comp, m_B).slots[0] == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 2); + + BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1); + BOOST_TEST(get_method(comp, m_C).slots[0] == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 2); +} + +BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1) { + using test_policy = test_policy_<__COUNTER__>; + + // A1 + // / \ + // B1 C1 - slots 0-2 are wasted + // \ / + // D1 + + struct A {}; + struct B : virtual A {}; + struct C : virtual A {}; + struct D : B, C {}; + + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + ADD_METHOD(A); + ADD_METHOD(B); + ADD_METHOD(C); + ADD_METHOD(D); + auto comp = update(); + + BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1); + BOOST_TEST(get_method(comp, m_A).slots[0] == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); + + BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1); + BOOST_TEST(get_method(comp, m_B).slots[0] == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 2); + + BOOST_TEST_REQUIRE(get_method(comp, m_D).slots.size() == 1); + BOOST_TEST(get_method(comp, m_D).slots[0] == 2); + BOOST_TEST(get_class(comp)->vtbl.size() == 4); + + BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1); + BOOST_TEST(get_method(comp, m_C).slots[0] == 3); + BOOST_TEST(get_class(comp)->vtbl.size() == 4); + // slots 0-2 in C are wasted, to make room for methods in B and D +} + +BOOST_AUTO_TEST_CASE(test_assign_slots_a1_b1_d1_c1_d1_e2) { + using test_policy = test_policy_<__COUNTER__>; + + // A1 + // / \ + // B1 C1 slots 0-2 are wasted + // \ / \ + // D1 E2 but E can use them + + struct A {}; + struct B : virtual A {}; + struct C : virtual A {}; + struct E : C {}; + struct D : B, C {}; + + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + ADD_METHOD(A); + ADD_METHOD(B); + ADD_METHOD(C); + ADD_METHOD(D); + ADD_METHOD_N(E, 1); + ADD_METHOD_N(E, 2); + ADD_METHOD_N(E, 3); + auto comp = update(); + + BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1); + BOOST_TEST(get_method(comp, m_A).slots[0] == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); + + BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1); + BOOST_TEST(get_method(comp, m_B).slots[0] == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 2); + + BOOST_TEST_REQUIRE(get_method(comp, m_D).slots.size() == 1); + BOOST_TEST(get_method(comp, m_D).slots[0] == 2); + BOOST_TEST(get_class(comp)->vtbl.size() == 4); + + BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1); + BOOST_TEST(get_method(comp, m_C).slots[0] == 3); + BOOST_TEST(get_class(comp)->vtbl.size() == 4); + // slots 0-2 in C are wasted, to make room for methods in B and D + + BOOST_TEST_REQUIRE(get_method(comp, m_E1).slots.size() == 1); + BOOST_TEST(get_method(comp, m_E1).slots[0] == 1); + + BOOST_TEST_REQUIRE(get_method(comp, m_E2).slots.size() == 1); + BOOST_TEST(get_method(comp, m_E2).slots[0] == 2); + + BOOST_TEST_REQUIRE(get_method(comp, m_E3).slots.size() == 1); + BOOST_TEST(get_method(comp, m_E3).slots[0] == 4); + + BOOST_TEST(get_class(comp)->vtbl.size() == 5); } -} // namespace multiple_inheritance +BOOST_AUTO_TEST_CASE(test_assign_slots_a1_c1_b1) { + using test_policy = test_policy_<__COUNTER__>; + + // A1 B1 + // \ / + // C1 + + struct A {}; + struct B {}; + struct C : A, B {}; + + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + YOMM2_STATIC(use_classes); + ADD_METHOD(A); + ADD_METHOD(B); + ADD_METHOD(C); + auto comp = update(); + + BOOST_TEST_REQUIRE(get_method(comp, m_A).slots.size() == 1); + BOOST_TEST(get_method(comp, m_A).slots[0] == 0); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); + + BOOST_TEST_REQUIRE(get_method(comp, m_C).slots.size() == 1); + BOOST_TEST(get_method(comp, m_C).slots[0] == 1); + BOOST_TEST(get_class(comp)->vtbl.size() == 3); + + BOOST_TEST_REQUIRE(get_method(comp, m_B).slots.size() == 1); + BOOST_TEST(get_method(comp, m_B).slots[0] == 2); + BOOST_TEST(get_class(comp)->first_slot == 2); + BOOST_TEST(get_class(comp)->vtbl.size() == 1); +} diff --git a/tests/test_generator_forward_decls.cpp b/tests/test_generator_forward_decls.cpp index d5371c7e..b1593967 100644 --- a/tests/test_generator_forward_decls.cpp +++ b/tests/test_generator_forward_decls.cpp @@ -195,13 +195,14 @@ BOOST_AUTO_TEST_CASE(test_generate_offsets) { YOMM2_STATIC(baz2::add_function); update(); + { std::ostringstream os; os << "\n"; generator gen; gen.write_static_offsets(os); std::string_view expected = R"( -template<> struct yorel::yomm2::detail::static_offsets, int), test_policy_<1>>> {static constexpr size_t slots[] = {2}; }; +template<> struct yorel::yomm2::detail::static_offsets, int), test_policy_<1>>> {static constexpr size_t slots[] = {0}; }; )"; auto actual = std::regex_replace(os.str(), std::basic_regex("> +>"), ">>"); @@ -217,8 +218,8 @@ template<> struct yorel::yomm2::detail::static_offsets(os); std::string_view expected = R"( -template<> struct yorel::yomm2::detail::static_offsets, yorel::yomm2::virtual_), test_policy_<1>>> {static constexpr size_t slots[] = {0, 1}; static constexpr size_t strides[] = {1}; }; -template<> struct yorel::yomm2::detail::static_offsets, int), test_policy_<1>>> {static constexpr size_t slots[] = {2}; }; +template<> struct yorel::yomm2::detail::static_offsets, int), test_policy_<1>>> {static constexpr size_t slots[] = {0}; }; +template<> struct yorel::yomm2::detail::static_offsets, yorel::yomm2::virtual_), test_policy_<1>>> {static constexpr size_t slots[] = {1, 2}; static constexpr size_t strides[] = {1}; }; )"; auto actual = std::regex_replace(os.str(), std::basic_regex("> +>"), ">>"); diff --git a/tests/test_virtual_ptr_all.cpp b/tests/test_virtual_ptr_all.cpp index 2610d5d8..d9d26f32 100644 --- a/tests/test_virtual_ptr_all.cpp +++ b/tests/test_virtual_ptr_all.cpp @@ -71,7 +71,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE( update(); using vptr_player = virtual_ptr; - static_assert(detail::is_virtual_ptr); + static_assert(is_virtual_ptr); using vptr_cat = virtual_ptr; Player player;