File indexing completed on 2026-08-17 08:52:05
0001
0002
0003
0004
0005
0006 #ifndef BOOST_OPENMETHOD_COMPILER_HPP
0007 #define BOOST_OPENMETHOD_COMPILER_HPP
0008
0009 #include <boost/openmethod/core.hpp>
0010 #include <boost/openmethod/detail/ostdstream.hpp>
0011
0012 #include <algorithm>
0013 #include <cstdint>
0014 #include <deque>
0015 #include <map>
0016 #include <memory>
0017 #include <numeric>
0018 #include <string>
0019 #include <unordered_map>
0020 #include <unordered_set>
0021 #include <utility>
0022 #include <vector>
0023
0024 #include <boost/assert.hpp>
0025 #include <boost/dynamic_bitset.hpp>
0026
0027 #ifdef _MSC_VER
0028 #pragma warning(push)
0029 #pragma warning(disable : 4456)
0030 #pragma warning(disable : 4458)
0031 #pragma warning(disable : 4702)
0032 #endif
0033
0034 namespace boost::openmethod {
0035
0036 namespace detail {
0037
0038 template<class Reports, class Facets, typename = void>
0039 struct aggregate_reports;
0040
0041 template<class... Reports, class Facet, class... MoreFacets>
0042 struct aggregate_reports<
0043 mp11::mp_list<Reports...>, mp11::mp_list<Facet, MoreFacets...>,
0044 std::void_t<typename Facet::report>> {
0045 using type = typename aggregate_reports<
0046 mp11::mp_list<Reports..., typename Facet::report>,
0047 mp11::mp_list<MoreFacets...>>::type;
0048 };
0049
0050 template<class... Reports, class Facet, class... MoreFacets, typename Void>
0051 struct aggregate_reports<
0052 mp11::mp_list<Reports...>, mp11::mp_list<Facet, MoreFacets...>, Void> {
0053 using type = typename aggregate_reports<
0054 mp11::mp_list<Reports...>, mp11::mp_list<MoreFacets...>>::type;
0055 };
0056
0057 template<class... Reports, typename Void>
0058 struct aggregate_reports<mp11::mp_list<Reports...>, mp11::mp_list<>, Void> {
0059 struct type : Reports... {};
0060 };
0061
0062 inline void merge_into(boost::dynamic_bitset<>& a, boost::dynamic_bitset<>& b) {
0063 if (b.size() < a.size()) {
0064 b.resize(a.size());
0065 }
0066
0067 for (std::size_t i = 0; i < a.size(); ++i) {
0068 if (a[i]) {
0069 b[i] = true;
0070 }
0071 }
0072 }
0073
0074 inline void set_bit(boost::dynamic_bitset<>& mask, std::size_t bit) {
0075 if (bit >= mask.size()) {
0076 mask.resize(bit + 1);
0077 }
0078
0079 mask[bit] = true;
0080 }
0081
0082 struct generic_compiler {
0083
0084 struct method;
0085
0086 struct parameter {
0087 struct method* method;
0088 std::size_t param;
0089 };
0090
0091 struct vtbl_entry {
0092 std::size_t method_index, vp_index, group_index;
0093 };
0094
0095 struct class_ {
0096 bool is_abstract = false;
0097 std::vector<type_id> type_ids;
0098 std::vector<class_*> transitive_bases;
0099 std::vector<class_*> direct_bases;
0100 std::vector<class_*> direct_derived;
0101 std::unordered_set<class_*> transitive_derived;
0102 std::vector<parameter> used_by_vp;
0103 boost::dynamic_bitset<> used_slots;
0104 boost::dynamic_bitset<> reserved_slots;
0105 std::size_t first_slot = 0;
0106 std::size_t mark = 0;
0107 std::vector<vtbl_entry> vtbl;
0108 vptr_type* static_vptr;
0109
0110 auto is_base_of(class_* other) const -> bool {
0111 return transitive_derived.find(other) != transitive_derived.end();
0112 }
0113
0114 auto vptr() const -> const vptr_type& {
0115 return *static_vptr;
0116 }
0117
0118 auto type_id_begin() const {
0119 return type_ids.begin();
0120 }
0121
0122 auto type_id_end() const {
0123 return type_ids.end();
0124 }
0125 };
0126
0127 struct overrider {
0128 detail::overrider_info* info = nullptr;
0129 overrider* next = nullptr;
0130 std::vector<class_*> vp;
0131 class_* covariant_return_type = nullptr;
0132 void (*pf)();
0133 std::size_t method_index, spec_index;
0134 };
0135
0136 using bitvec = boost::dynamic_bitset<>;
0137
0138 struct group {
0139 std::vector<class_*> classes;
0140 bool has_concrete_classes{false};
0141 };
0142
0143 using group_map = std::map<bitvec, group>;
0144
0145 struct method_report {
0146 std::size_t cells = 0;
0147 std::size_t not_implemented = 0;
0148 std::size_t ambiguous = 0;
0149 };
0150
0151 struct report : method_report {};
0152
0153 static void accumulate(const method_report& partial, report& total);
0154
0155 struct method {
0156 detail::method_info* info;
0157 std::vector<class_*> vp;
0158 class_* covariant_return_type = nullptr;
0159 std::vector<overrider> overriders;
0160 std::vector<std::size_t> slots;
0161 std::vector<std::size_t> strides;
0162 std::vector<const overrider*> dispatch_table;
0163
0164
0165 overrider not_implemented;
0166 overrider ambiguous;
0167 vptr_type gv_dispatch_table = nullptr;
0168 auto arity() const {
0169 return vp.size();
0170 }
0171 method_report report;
0172 };
0173
0174 const method* operator[](const detail::method_info& info) const {
0175 auto iter = std::find_if(
0176 methods.begin(), methods.end(),
0177 [&info](const method& m) { return m.info == &info; });
0178
0179 if (iter != methods.end()) {
0180 return &*iter;
0181 }
0182
0183 return nullptr;
0184 }
0185
0186 std::deque<class_> classes;
0187
0188 auto classes_begin() const {
0189 return classes.begin();
0190 }
0191
0192 auto classes_end() const {
0193 return classes.end();
0194 }
0195
0196 std::vector<method> methods;
0197 std::size_t class_mark = 0;
0198 bool compilation_done = false;
0199 };
0200
0201 template<class Compiler>
0202 struct trace_stream {
0203 bool on = false;
0204 std::size_t indentation_level{0};
0205
0206 auto operator++() -> trace_stream& {
0207 if constexpr (Compiler::has_trace) {
0208 if (on) {
0209 for (std::size_t i = 0; i < indentation_level; ++i) {
0210 Compiler::Registry::output::os << " ";
0211 }
0212 }
0213 }
0214
0215 return *this;
0216 }
0217
0218 struct indent {
0219 trace_stream& trace;
0220 int by;
0221
0222 explicit indent(trace_stream& trace, int by = 2)
0223 : trace(trace), by(by) {
0224 trace.indentation_level += by;
0225 }
0226
0227 ~indent() {
0228 trace.indentation_level -= by;
0229 }
0230 };
0231 };
0232
0233 struct rflush {
0234 std::size_t width;
0235 std::size_t value;
0236 explicit rflush(std::size_t width, std::size_t value)
0237 : width(width), value(value) {
0238 }
0239 };
0240
0241 struct type_name {
0242 type_name(type_id type) : type(type) {
0243 }
0244 type_id type;
0245 };
0246
0247 template<class Compiler>
0248 auto operator<<(trace_stream<Compiler>& tr, const generic_compiler::class_& cls)
0249 -> trace_stream<Compiler>& {
0250 if constexpr (Compiler::has_trace) {
0251 tr << type_name(cls.type_ids[0]);
0252 }
0253
0254 return tr;
0255 }
0256
0257 template<class Compiler, template<typename...> class Container, typename... T>
0258 auto operator<<(
0259 trace_stream<Compiler>& tr,
0260 Container<generic_compiler::class_*, T...>& classes)
0261 -> trace_stream<Compiler>& {
0262 if constexpr (Compiler::has_trace) {
0263 tr << "(";
0264 const char* sep = "";
0265 for (auto cls : classes) {
0266 tr << sep << *cls;
0267 sep = ", ";
0268 }
0269
0270 tr << ")";
0271 }
0272
0273 return tr;
0274 }
0275
0276 struct spec_name {
0277 spec_name(
0278 const detail::generic_compiler::method& method,
0279 const detail::generic_compiler::overrider* def)
0280 : method(method), def(def) {
0281 }
0282 const detail::generic_compiler::method& method;
0283 const detail::generic_compiler::overrider* def;
0284 };
0285
0286 template<class Compiler>
0287 auto operator<<(trace_stream<Compiler>& tr, const spec_name& sn)
0288 -> trace_stream<Compiler>& {
0289 if (sn.def == &sn.method.not_implemented) {
0290 tr << "not implemented";
0291 } else if (sn.def == &sn.method.ambiguous) {
0292 tr << "ambiguous";
0293 } else {
0294 tr << type_name(sn.def->info->type);
0295 }
0296
0297 return tr;
0298 }
0299
0300 template<typename Iterator>
0301 struct range;
0302
0303 template<class Compiler, typename T, typename F>
0304 auto write_range(trace_stream<Compiler>& tr, range<T> range, F fn) -> auto& {
0305 if constexpr (Compiler::has_trace) {
0306 if (tr.on) {
0307 tr << "(";
0308 const char* sep = "";
0309 for (auto value : range) {
0310 tr << sep << fn(value);
0311 sep = ", ";
0312 }
0313
0314 tr << ")";
0315 }
0316 }
0317
0318 return tr;
0319 }
0320
0321 template<class Compiler, typename T>
0322 auto operator<<(trace_stream<Compiler>& tr, const T& value) -> auto& {
0323 if constexpr (Compiler::has_trace) {
0324 if (tr.on) {
0325 Compiler::Registry::output::os << value;
0326 }
0327 }
0328 return tr;
0329 }
0330
0331 template<class Compiler>
0332 auto operator<<(trace_stream<Compiler>& tr, const rflush& rf) -> auto& {
0333 if constexpr (Compiler::has_trace) {
0334 if (tr.on) {
0335 std::size_t digits = 1;
0336 auto tmp = rf.value / 10;
0337
0338 while (tmp) {
0339 ++digits;
0340 tmp /= 10;
0341 }
0342
0343 while (digits < rf.width) {
0344 tr << " ";
0345 ++digits;
0346 }
0347
0348 tr << rf.value;
0349 }
0350 }
0351
0352 return tr;
0353 }
0354
0355 template<class Compiler>
0356 auto operator<<(trace_stream<Compiler>& tr, const boost::dynamic_bitset<>& bits)
0357 -> auto& {
0358 if constexpr (Compiler::has_trace) {
0359 if (tr.on) {
0360 auto i = bits.size();
0361 while (i != 0) {
0362 --i;
0363 Compiler::Registry::output::os << bits[i];
0364 }
0365 }
0366 }
0367
0368 return tr;
0369 }
0370
0371 template<class Compiler>
0372 auto operator<<(trace_stream<Compiler>& tr, const range<type_id*>& tips)
0373 -> auto& {
0374 return write_range(tr, tips, [](auto tip) { return type_name(tip); });
0375 }
0376
0377 template<class Compiler, typename T>
0378 auto operator<<(trace_stream<Compiler>& tr, const range<T>& range) -> auto& {
0379 return write_range(tr, range, [](auto value) { return value; });
0380 }
0381
0382 template<class Compiler>
0383 auto operator<<(trace_stream<Compiler>& tr, const type_name& manip) -> auto& {
0384 if constexpr (Compiler::has_trace) {
0385 Compiler::Registry::rtti::type_name(manip.type, tr);
0386 }
0387
0388 return tr;
0389 }
0390 }
0391
0392
0393 template<class... Policies>
0394 template<class... Options>
0395 struct registry<Policies...>::compiler : detail::generic_compiler {
0396 using type_index_type = decltype(rtti::type_index(0));
0397
0398 typename detail::aggregate_reports<mp11::mp_list<report>, policy_list>::type
0399 report;
0400
0401 std::unordered_map<type_index_type, class_*> class_map;
0402
0403 using Registry = registry;
0404
0405 compiler(Options... opts);
0406
0407 auto compile();
0408 void initialize();
0409 void install_global_tables();
0410
0411 void augment_classes();
0412 void collect_transitive_bases(class_* cls, class_* base);
0413 void calculate_transitive_derived(class_& cls);
0414 void augment_methods();
0415 void assign_slots();
0416 void assign_tree_slots(class_& cls, std::size_t base_slot);
0417 void assign_lattice_slots(class_& cls);
0418 void build_dispatch_tables();
0419 void build_dispatch_table(
0420 method& m, std::size_t dim,
0421 std::vector<group_map>::const_iterator group, const bitvec& candidates,
0422 bool concrete);
0423 void write_global_data();
0424 void print(const method_report& report) const;
0425 static void select_dominant_overriders(
0426 std::vector<overrider*>& dominants, std::size_t& pick,
0427 std::size_t& remaining);
0428 static auto
0429 is_more_specific(const overrider* a, const overrider* b) -> bool;
0430 static auto is_base(const overrider* a, const overrider* b) -> bool;
0431
0432 std::tuple<Options...> options;
0433
0434 template<class Option>
0435 static constexpr bool has_option =
0436 mp11::mp_contains<mp11::mp_list<Options...>, Option>::value;
0437
0438 static constexpr bool has_trace = has_option<trace>;
0439 static constexpr bool has_n2216 = has_option<n2216>;
0440
0441 mutable detail::trace_stream<compiler> tr;
0442 using indent = typename detail::trace_stream<compiler>::indent;
0443 };
0444
0445 template<class... Policies>
0446 template<class... Options>
0447 void registry<Policies...>::compiler<Options...>::install_global_tables() {
0448 if (!compilation_done) {
0449 abort();
0450 }
0451
0452 write_global_data();
0453
0454 print(report);
0455 ++tr << "Finished\n";
0456 }
0457
0458 template<class... Policies>
0459 template<class... Options>
0460 auto registry<Policies...>::compiler<Options...>::compile() {
0461 augment_classes();
0462 augment_methods();
0463 assign_slots();
0464 build_dispatch_tables();
0465
0466 compilation_done = true;
0467
0468 return report;
0469 }
0470
0471 template<class... Policies>
0472 template<class... Options>
0473 void registry<Policies...>::compiler<Options...>::initialize() {
0474 compile();
0475 install_global_tables();
0476 registry<Policies...>::initialized = true;
0477 }
0478
0479 #ifdef _MSC_VER
0480 namespace detail {
0481
0482 template<bool HasTrace, typename T>
0483 struct msvc_tuple_get;
0484
0485 template<typename T>
0486 struct msvc_tuple_get<true, T> {
0487 template<class Tuple>
0488 static decltype(auto) fn(const Tuple& t) {
0489 return std::get<T>(t);
0490 }
0491 };
0492
0493 template<typename T>
0494 struct msvc_tuple_get<false, T> {
0495 template<class Tuple>
0496 static decltype(auto) fn(const Tuple&) {
0497 return T();
0498 }
0499 };
0500 }
0501 #endif
0502
0503 template<class... Policies>
0504 template<class... Options>
0505 registry<Policies...>::compiler<Options...>::compiler(Options... opts)
0506 : options(opts...) {
0507 if constexpr (has_trace) {
0508 #ifdef _MSC_VER
0509 tr.on = detail::msvc_tuple_get<has_trace, trace>::fn(options).on;
0510 #else
0511
0512 tr.on = std::get<trace>(options).on;
0513 #endif
0514 }
0515 }
0516
0517 template<class... Policies>
0518 template<class... Options>
0519 void registry<Policies...>::compiler<Options...>::collect_transitive_bases(
0520 class_* cls, class_* base) {
0521 if (base->mark == class_mark) {
0522 return;
0523 }
0524
0525 cls->transitive_bases.push_back(base);
0526 base->mark = class_mark;
0527
0528 for (auto base_base : base->transitive_bases) {
0529 collect_transitive_bases(cls, base_base);
0530 }
0531 }
0532
0533 template<class... Policies>
0534 template<class... Options>
0535 void registry<Policies...>::compiler<Options...>::augment_classes() {
0536 using namespace detail;
0537
0538
0539 {
0540 ++tr << "Static class info:\n";
0541
0542
0543
0544
0545 for (auto& cr : registry::classes) {
0546 if constexpr (has_deferred_static_rtti) {
0547 static_cast<deferred_class_info&>(cr).resolve_type_ids();
0548 }
0549
0550 {
0551 indent _(tr);
0552 ++tr << type_name(cr.type) << ": "
0553 << range{cr.first_base, cr.last_base} << "\n";
0554 }
0555
0556 auto& rtc = class_map[rtti::type_index(cr.type)];
0557
0558 if (rtc == nullptr) {
0559 rtc = &classes.emplace_back();
0560 rtc->is_abstract = cr.is_abstract;
0561 rtc->static_vptr = cr.static_vptr;
0562 }
0563
0564 if (std::find(
0565 rtc->type_ids.begin(), rtc->type_ids.end(), cr.type) ==
0566 rtc->type_ids.end()) {
0567 rtc->type_ids.push_back(cr.type);
0568 }
0569 }
0570 }
0571
0572
0573
0574
0575 for (auto& cr : registry::classes) {
0576 auto rtc = class_map[rtti::type_index(cr.type)];
0577
0578 for (auto& base : range{cr.first_base, cr.last_base}) {
0579 auto rtb = class_map[rtti::type_index(base)];
0580
0581 if (!rtb) {
0582 missing_class error;
0583 error.type = base;
0584
0585 if constexpr (has_error_handler) {
0586 error_handler::error(error);
0587 }
0588
0589 abort();
0590 }
0591
0592 if (rtc != rtb) {
0593
0594
0595 ++class_mark;
0596 collect_transitive_bases(rtc, rtb);
0597 }
0598 }
0599 }
0600
0601
0602
0603
0604 std::size_t mark = ++class_mark;
0605
0606 for (auto& rtc : classes) {
0607 decltype(rtc.transitive_bases) bases;
0608 mark = ++class_mark;
0609
0610 for (auto rtb : rtc.transitive_bases) {
0611 if (rtb->mark != mark) {
0612 bases.push_back(rtb);
0613 rtb->mark = mark;
0614 }
0615 }
0616
0617 rtc.transitive_bases.swap(bases);
0618 }
0619
0620 for (auto& rtc : classes) {
0621
0622
0623 std::sort(
0624 rtc.transitive_bases.begin(), rtc.transitive_bases.end(),
0625 [](auto a, auto b) {
0626 return a->transitive_bases.size() > b->transitive_bases.size();
0627 });
0628 mark = ++class_mark;
0629
0630
0631
0632
0633
0634
0635 for (auto rtb : rtc.transitive_bases) {
0636 if (rtb->mark == mark) {
0637 continue;
0638 }
0639
0640 rtc.direct_bases.push_back(rtb);
0641
0642 for (auto rtbb : rtb->transitive_bases) {
0643 rtbb->mark = mark;
0644 }
0645 }
0646 }
0647
0648 for (auto& rtc : classes) {
0649 for (auto rtb : rtc.direct_bases) {
0650 rtb->direct_derived.push_back(&rtc);
0651 }
0652 }
0653
0654 for (auto& rtc : classes) {
0655 calculate_transitive_derived(rtc);
0656 }
0657
0658 if constexpr (has_trace) {
0659 ++tr << "Inheritance lattice:\n";
0660
0661 for (auto& rtc : classes) {
0662 indent _2(tr);
0663 ++tr << rtc << "\n";
0664
0665 {
0666 indent _3(tr);
0667 ++tr << "bases: " << rtc.direct_bases << "\n";
0668 ++tr << "derived: " << rtc.direct_derived << "\n";
0669 ++tr << "covariant: " << rtc.transitive_derived << "\n";
0670 }
0671 }
0672 }
0673 }
0674
0675 template<class... Policies>
0676 template<class... Options>
0677 void registry<Policies...>::compiler<Options...>::calculate_transitive_derived(
0678 class_& cls) {
0679 if (!cls.transitive_derived.empty()) {
0680 return;
0681 }
0682
0683 cls.transitive_derived.insert(&cls);
0684
0685 for (auto derived : cls.direct_derived) {
0686 if (derived->transitive_derived.empty()) {
0687 calculate_transitive_derived(*derived);
0688 }
0689
0690 std::copy(
0691 derived->transitive_derived.begin(),
0692 derived->transitive_derived.end(),
0693 std::inserter(
0694 cls.transitive_derived, cls.transitive_derived.end()));
0695 }
0696 }
0697
0698 template<class... Policies>
0699 template<class... Options>
0700 void registry<Policies...>::compiler<Options...>::augment_methods() {
0701 using namespace policies;
0702 using namespace detail;
0703
0704 methods.resize(registry::methods.size());
0705
0706 ++tr << "Methods:\n";
0707 indent _(tr);
0708
0709 auto meth_iter = methods.begin();
0710
0711 for (auto& meth_info : registry::methods) {
0712 if constexpr (has_deferred_static_rtti) {
0713 static_cast<deferred_method_info&>(meth_info).resolve_type_ids();
0714 }
0715
0716 ++tr << type_name(meth_info.method_type_id) << " "
0717 << range{meth_info.vp_begin, meth_info.vp_end} << "\n";
0718
0719 indent _(tr);
0720
0721 meth_iter->info = &meth_info;
0722 meth_iter->vp.reserve(meth_info.arity());
0723 meth_iter->slots.resize(meth_info.arity());
0724
0725 {
0726 std::size_t param_index = 0;
0727
0728 for (auto ti : range{meth_info.vp_begin, meth_info.vp_end}) {
0729 auto class_ = class_map[rtti::type_index(ti)];
0730 if (!class_) {
0731 ++tr << "unknown class " << ti << "(" << type_name(ti)
0732 << ") for parameter #" << (param_index + 1) << "\n";
0733 missing_class error;
0734 error.type = ti;
0735
0736 if constexpr (has_error_handler) {
0737 error_handler::error(error);
0738 }
0739
0740 abort();
0741 }
0742
0743 meth_iter->vp.push_back(class_);
0744 }
0745 }
0746
0747 if (rtti::type_index(meth_info.return_type_id) !=
0748 rtti::type_index(rtti::template static_type<void>())) {
0749 auto covariant_return_iter =
0750 class_map.find(rtti::type_index(meth_info.return_type_id));
0751
0752 if (covariant_return_iter != class_map.end()) {
0753 meth_iter->covariant_return_type =
0754 covariant_return_iter->second;
0755 }
0756 }
0757
0758
0759
0760 const auto method_index = meth_iter - methods.begin();
0761 auto spec_size = meth_info.overriders.size();
0762 meth_iter->not_implemented.pf = meth_iter->info->not_implemented;
0763 meth_iter->not_implemented.method_index = method_index;
0764 meth_iter->not_implemented.spec_index = spec_size;
0765 meth_iter->ambiguous.pf = meth_iter->info->ambiguous;
0766 meth_iter->ambiguous.method_index = method_index;
0767 meth_iter->ambiguous.spec_index = spec_size + 1;
0768
0769 meth_iter->overriders.resize(spec_size);
0770 auto spec_iter = meth_iter->overriders.begin();
0771
0772 for (auto& overrider_info : meth_info.overriders) {
0773 if constexpr (has_deferred_static_rtti) {
0774 static_cast<deferred_overrider_info&>(overrider_info)
0775 .resolve_type_ids();
0776 }
0777
0778 spec_iter->method_index = method_index;
0779 spec_iter->spec_index = spec_iter - meth_iter->overriders.begin();
0780
0781 ++tr << type_name(overrider_info.type) << " (" << overrider_info.pf
0782 << ")\n";
0783 spec_iter->info = &overrider_info;
0784 spec_iter->vp.reserve(meth_info.arity());
0785 std::size_t param_index = 0;
0786
0787 for (auto type :
0788 range{overrider_info.vp_begin, overrider_info.vp_end}) {
0789 indent _(tr);
0790 auto class_ = class_map[rtti::type_index(type)];
0791
0792 if (!class_) {
0793 ++tr << "unknown class error for *virtual* parameter #"
0794 << (param_index + 1) << "\n";
0795 missing_class error;
0796 error.type = type;
0797
0798 if constexpr (has_error_handler) {
0799 error_handler::error(error);
0800 }
0801
0802 abort();
0803 }
0804
0805 spec_iter->pf = spec_iter->info->pf;
0806 spec_iter->vp.push_back(class_);
0807 }
0808
0809 if (meth_iter->covariant_return_type) {
0810 auto covariant_return_iter = class_map.find(
0811 rtti::type_index(overrider_info.return_type));
0812
0813 if (covariant_return_iter != class_map.end()) {
0814 spec_iter->covariant_return_type =
0815 covariant_return_iter->second;
0816 } else {
0817 missing_class error;
0818 error.type = overrider_info.return_type;
0819
0820 if constexpr (has_error_handler) {
0821 error_handler::error(error);
0822 }
0823
0824 abort();
0825 }
0826 }
0827
0828 ++spec_iter;
0829 }
0830
0831 ++meth_iter;
0832 }
0833
0834 for (auto& method : methods) {
0835 std::size_t param_index = 0;
0836
0837 for (auto vp : method.vp) {
0838 for (auto& overrider : method.overriders) {
0839 if (overrider.vp[param_index] == vp) {
0840 continue;
0841 }
0842
0843 if (!vp->is_base_of(overrider.vp[param_index])) {
0844 missing_base error;
0845 error.base = overrider.vp[param_index]->type_ids[0];
0846 error.derived = vp->type_ids[0];
0847
0848 if constexpr (has_error_handler) {
0849 error_handler::error(error);
0850 }
0851
0852 abort();
0853 }
0854 }
0855
0856 vp->used_by_vp.push_back({&method, param_index++});
0857 }
0858 }
0859 }
0860
0861 template<class... Policies>
0862 template<class... Options>
0863 void registry<Policies...>::compiler<Options...>::assign_slots() {
0864 ++tr << "Allocating slots...\n";
0865
0866 {
0867 indent _(tr);
0868
0869 ++class_mark;
0870
0871 for (auto& cls : classes) {
0872 if (cls.direct_bases.size() == 0) {
0873 if (std::find_if(
0874 cls.transitive_derived.begin(),
0875 cls.transitive_derived.end(), [](auto cls) {
0876 return cls->direct_bases.size() > 1;
0877 }) == cls.transitive_derived.end()) {
0878 indent _(tr);
0879 assign_tree_slots(cls, 0);
0880 } else {
0881 assign_lattice_slots(cls);
0882 }
0883 }
0884 }
0885 }
0886
0887 ++tr << "Allocating MI v-tables...\n";
0888
0889 {
0890 indent _(tr);
0891
0892 for (auto& cls : classes) {
0893 if (cls.used_slots.empty()) {
0894
0895 continue;
0896 }
0897
0898 auto first_slot = cls.used_slots.find_first();
0899 cls.first_slot =
0900 first_slot == boost::dynamic_bitset<>::npos ? 0u : first_slot;
0901 cls.vtbl.resize(cls.used_slots.size() - cls.first_slot);
0902 ++tr << cls << " vtbl: " << cls.first_slot << "-"
0903 << cls.used_slots.size() << " slots " << cls.used_slots
0904 << "\n";
0905 }
0906 }
0907 }
0908
0909 template<class... Policies>
0910 template<class... Options>
0911 void registry<Policies...>::compiler<Options...>::assign_tree_slots(
0912 class_& cls, std::size_t base_slot) {
0913 auto next_slot = base_slot;
0914 using namespace detail;
0915
0916 for (const auto& mp : cls.used_by_vp) {
0917 ++tr << " in " << cls << " for "
0918 << type_name(mp.method->info->method_type_id) << " parameter "
0919 << mp.param << ": " << next_slot << "\n";
0920 mp.method->slots[mp.param] = next_slot++;
0921 }
0922
0923 cls.first_slot = 0;
0924 cls.vtbl.resize(next_slot);
0925
0926 for (auto pd : cls.direct_derived) {
0927 assign_tree_slots(*pd, next_slot);
0928 }
0929 }
0930
0931 template<class... Policies>
0932 template<class... Options>
0933 void registry<Policies...>::compiler<Options...>::assign_lattice_slots(
0934 class_& cls) {
0935 using namespace detail;
0936
0937 if (cls.mark == class_mark) {
0938 return;
0939 }
0940
0941 cls.mark = class_mark;
0942
0943 if (!cls.used_by_vp.empty()) {
0944 for (const auto& mp : cls.used_by_vp) {
0945 ++tr << " in " << cls << " for "
0946 << type_name(mp.method->info->method_type_id) << " parameter "
0947 << mp.param << "\n";
0948
0949 indent _(tr);
0950
0951 ++tr << "reserved slots: " << cls.reserved_slots
0952 << " used slots: " << cls.used_slots << "\n";
0953
0954 auto unavailable_slots = cls.used_slots;
0955 detail::merge_into(cls.reserved_slots, unavailable_slots);
0956
0957 ++tr << "unavailable slots: " << unavailable_slots << "\n";
0958
0959 std::size_t slot = 0;
0960
0961 for (; slot < unavailable_slots.size(); ++slot) {
0962 if (!unavailable_slots[slot]) {
0963 break;
0964 }
0965 }
0966
0967 ++tr << "first available slot: " << slot << "\n";
0968
0969 mp.method->slots[mp.param] = slot;
0970 detail::set_bit(cls.used_slots, slot);
0971 detail::set_bit(cls.reserved_slots, slot);
0972
0973 {
0974 ++tr << "reserve slots " << cls.used_slots << " in:\n";
0975 indent _(tr);
0976
0977 for (auto base : cls.transitive_bases) {
0978 ++tr << *base << "\n";
0979 detail::merge_into(cls.used_slots, base->reserved_slots);
0980 }
0981 }
0982
0983 {
0984 ++tr << "assign slots " << cls.used_slots << " in:\n";
0985 indent _(tr);
0986
0987 for (auto covariant : cls.transitive_derived) {
0988 if (&cls != covariant) {
0989 ++tr << *covariant << "\n";
0990 detail::merge_into(
0991 cls.used_slots, covariant->used_slots);
0992
0993 for (auto base : covariant->transitive_bases) {
0994 ++tr << *base << "\n";
0995 detail::merge_into(
0996 cls.used_slots, base->reserved_slots);
0997 }
0998 }
0999 }
1000 }
1001 }
1002 }
1003
1004 for (auto pd : cls.direct_derived) {
1005 assign_lattice_slots(*pd);
1006 }
1007 }
1008
1009 template<class... Policies>
1010 template<class... Options>
1011 void registry<Policies...>::compiler<Options...>::build_dispatch_tables() {
1012 using namespace detail;
1013
1014 for (auto& m : methods) {
1015 ++tr << "Building dispatch table for "
1016 << type_name(m.info->method_type_id) << "\n";
1017 indent _(tr);
1018
1019 auto dims = m.arity();
1020
1021 std::vector<group_map> groups;
1022 groups.resize(dims);
1023
1024 {
1025 std::size_t dim = 0;
1026
1027 for (auto vp : m.vp) {
1028 auto& dim_group = groups[dim];
1029 ++tr << "make groups for param #" << dim << ", class " << *vp
1030 << "\n";
1031 indent _(tr);
1032
1033 for (auto covariant_class : vp->transitive_derived) {
1034 ++tr << "overriders applicable to " << *covariant_class
1035 << "\n";
1036 bitvec mask;
1037 mask.resize(m.overriders.size());
1038
1039 std::size_t group_index = 0;
1040 indent _2(tr);
1041
1042 for (auto& spec : m.overriders) {
1043 if (spec.vp[dim]->transitive_derived.find(
1044 covariant_class) !=
1045 spec.vp[dim]->transitive_derived.end()) {
1046 ++tr << type_name(spec.info->type) << "\n";
1047 mask[group_index] = 1;
1048 }
1049 ++group_index;
1050 }
1051
1052 auto& group = dim_group[mask];
1053 group.classes.push_back(covariant_class);
1054 group.has_concrete_classes = group.has_concrete_classes ||
1055 !covariant_class->is_abstract;
1056
1057 ++tr << "-> mask: " << mask << "\n";
1058 }
1059
1060 ++dim;
1061 }
1062 }
1063
1064 {
1065 std::size_t stride = 1;
1066 m.strides.reserve(dims - 1);
1067
1068 for (std::size_t dim = 1; dim < m.arity(); ++dim) {
1069 stride *= groups[dim - 1].size();
1070 ++tr << " stride for dim " << dim << " = " << stride << "\n";
1071 m.strides.push_back(stride);
1072 }
1073 }
1074
1075 for (std::size_t dim = 0; dim < m.arity(); ++dim) {
1076 indent _(tr);
1077 std::size_t group_num = 0;
1078
1079 for (auto& [mask, group] : groups[dim]) {
1080 ++tr << "groups for dim " << dim << ":\n";
1081 indent _(tr);
1082 ++tr << group_num << " mask " << mask << ":\n";
1083
1084 for (auto cls : group.classes) {
1085 indent _(tr);
1086 ++tr << type_name(cls->type_ids[0]) << "\n";
1087 auto& entry = cls->vtbl[m.slots[dim] - cls->first_slot];
1088 entry.method_index = &m - &methods[0];
1089 entry.vp_index = dim;
1090 entry.group_index = group_num;
1091 }
1092
1093 ++group_num;
1094 }
1095 }
1096
1097 {
1098 ++tr << "building dispatch table\n";
1099 bitvec all(m.overriders.size());
1100 all = ~all;
1101 build_dispatch_table(m, dims - 1, groups.end() - 1, all, true);
1102
1103 if (m.arity() > 1) {
1104 indent _(tr);
1105 m.report.cells = 1;
1106 ++tr << "dispatch table rank: ";
1107 const char* prefix = "";
1108
1109 for (const auto& dim_groups : groups) {
1110 m.report.cells *= dim_groups.size();
1111 tr << prefix << dim_groups.size();
1112 prefix = " x ";
1113 }
1114
1115 prefix = ", concrete only: ";
1116
1117 for (const auto& dim_groups : groups) {
1118 auto cells = std::count_if(
1119 dim_groups.begin(), dim_groups.end(),
1120 [](const auto& group) {
1121 return group.second.has_concrete_classes;
1122 });
1123 tr << prefix << cells;
1124 prefix = " x ";
1125 }
1126
1127 tr << "\n";
1128 }
1129
1130 print(m.report);
1131 accumulate(m.report, report);
1132 }
1133 }
1134 }
1135
1136 template<class... Policies>
1137 template<class... Options>
1138 void registry<Policies...>::compiler<Options...>::build_dispatch_table(
1139 method& m, std::size_t dim,
1140 std::vector<group_map>::const_iterator group_iter, const bitvec& candidates,
1141 bool concrete) {
1142 using namespace detail;
1143
1144 indent _(tr);
1145 std::size_t group_index = 0;
1146
1147 for (const auto& [group_mask, group] : *group_iter) {
1148 auto mask = candidates & group_mask;
1149
1150 if constexpr (has_trace) {
1151 ++tr << "group " << dim << "/" << group_index << " mask " << mask
1152 << "\n";
1153 indent _(tr);
1154 for (auto cls : range{group.classes.begin(), group.classes.end()}) {
1155 ++tr << type_name(cls->type_ids[0]) << "\n";
1156 }
1157 }
1158
1159 if (dim == 0) {
1160 std::vector<overrider*> overriders;
1161 std::size_t i = 0;
1162
1163 for (auto& spec : m.overriders) {
1164 if (mask[i]) {
1165 overriders.push_back(&spec);
1166 }
1167 ++i;
1168 }
1169
1170 if constexpr (has_trace) {
1171 ++tr << "select best of:\n";
1172 indent _(tr);
1173
1174 for (auto& app : overriders) {
1175 ++tr << "#" << app->spec_index << " "
1176 << type_name(app->info->type) << "\n";
1177 }
1178 }
1179
1180 std::vector<overrider*> dominants = overriders;
1181 std::size_t pick, remaining;
1182
1183 select_dominant_overriders(dominants, pick, remaining);
1184
1185 if (remaining == 0) {
1186 indent _(tr);
1187 ++tr << "not implemented\n";
1188 m.dispatch_table.push_back(&m.not_implemented);
1189 ++m.report.not_implemented;
1190 } else {
1191 if constexpr (!has_option<n2216>) {
1192 if (remaining > 1) {
1193 ++tr << "ambiguous\n";
1194 m.dispatch_table.push_back(&m.ambiguous);
1195 ++m.report.ambiguous;
1196 continue;
1197 }
1198 }
1199
1200 auto overrider = dominants[pick];
1201 m.dispatch_table.push_back(overrider);
1202 ++tr;
1203
1204 tr << "-> #" << overrider->spec_index << " "
1205 << type_name(overrider->info->type)
1206 << " pf = " << overrider->info->pf;
1207
1208 if (remaining > 1) {
1209 tr << " (ambiguous)";
1210 ++m.report.ambiguous;
1211 }
1212
1213 tr << "\n";
1214
1215
1216
1217
1218
1219
1220
1221 auto candidate = overriders.begin();
1222 remaining = 0;
1223
1224 for (auto dominant : dominants) {
1225 if (*candidate == dominant) {
1226 *candidate = nullptr;
1227 } else {
1228 ++remaining;
1229 }
1230
1231 ++candidate;
1232 }
1233
1234 if (remaining == 0) {
1235 ++tr << "no 'next'\n";
1236 overrider->next = &m.not_implemented;
1237 } else {
1238 if constexpr (has_trace) {
1239 ++tr << "for 'next', select best of:\n";
1240 indent _(tr);
1241
1242 for (auto& app : overriders) {
1243 if (app) {
1244 ++tr << "#" << app->spec_index << " "
1245 << type_name(app->info->type) << "\n";
1246 }
1247 }
1248 }
1249
1250 select_dominant_overriders(overriders, pick, remaining);
1251
1252 if constexpr (!has_option<n2216>) {
1253 if (remaining > 1) {
1254 ++tr << "ambiguous 'next'\n";
1255 overrider->next = &m.ambiguous;
1256 continue;
1257 }
1258 }
1259
1260 auto next_overrider = overriders[pick];
1261 overrider->next = next_overrider;
1262
1263 ++tr << "-> #" << next_overrider->spec_index << " "
1264 << type_name(next_overrider->info->type)
1265 << " pf = " << next_overrider->info->pf;
1266
1267 if (remaining > 1) {
1268 tr << " (ambiguous)";
1269
1270 }
1271
1272 tr << "\n";
1273 }
1274 }
1275 } else {
1276 build_dispatch_table(
1277 m, dim - 1, group_iter - 1, mask,
1278 concrete && group.has_concrete_classes);
1279 }
1280
1281 ++group_index;
1282 }
1283 }
1284
1285 inline void detail::generic_compiler::accumulate(
1286 const method_report& partial, report& total) {
1287 total.cells += partial.cells;
1288 total.not_implemented += partial.not_implemented != 0;
1289 total.ambiguous += partial.ambiguous != 0;
1290 }
1291
1292 template<class... Policies>
1293 template<class... Options>
1294 void registry<Policies...>::compiler<Options...>::write_global_data() {
1295 using namespace policies;
1296 using namespace detail;
1297
1298 auto dispatch_data_size = std::accumulate(
1299 methods.begin(), methods.end(), std::size_t(0),
1300 [](std::size_t sum, const method& m) {
1301
1302 return sum + m.dispatch_table.size();
1303 });
1304 dispatch_data_size = std::accumulate(
1305 classes.begin(), classes.end(), dispatch_data_size,
1306 [](auto sum, const auto& cls) { return sum + cls.vtbl.size(); });
1307
1308 std::vector<detail::word> new_dispatch_data(dispatch_data_size);
1309 auto gv_first = new_dispatch_data.data();
1310 [[maybe_unused]] auto gv_last = gv_first + dispatch_data_size;
1311 auto gv_iter = gv_first;
1312
1313 ++tr << "Initializing multi-method dispatch tables at " << gv_iter << "\n";
1314
1315 for (auto& m : methods) {
1316 if (m.info->arity() == 1) {
1317
1318 m.info->slots_strides_ptr[0] = m.slots[0];
1319 } else {
1320 auto strides_iter = std::copy(
1321 m.slots.begin(), m.slots.end(), m.info->slots_strides_ptr);
1322 std::copy(m.strides.begin(), m.strides.end(), strides_iter);
1323
1324 if constexpr (has_trace) {
1325 ++tr << rflush(4, dispatch_data_size) << " " << " method #"
1326 << m.dispatch_table[0]->method_index << " "
1327 << type_name(m.info->method_type_id) << "\n";
1328 indent _(tr);
1329
1330 for (auto& entry : m.dispatch_table) {
1331 ++tr << "spec #" << entry->spec_index << " "
1332 << spec_name(m, entry) << "\n";
1333 }
1334 }
1335
1336 m.gv_dispatch_table = gv_iter;
1337 BOOST_ASSERT(gv_iter + m.dispatch_table.size() <= gv_last);
1338 gv_iter = std::transform(
1339 m.dispatch_table.begin(), m.dispatch_table.end(), gv_iter,
1340 [](auto spec) { return spec->pf; });
1341 }
1342 }
1343
1344 ++tr << "Setting 'next' pointers\n";
1345
1346 for (auto& m : methods) {
1347 indent _(tr);
1348 ++tr << "method #" << " " << type_name(m.info->method_type_id) << "\n";
1349
1350 for (auto& overrider : m.overriders) {
1351 if (overrider.next) {
1352 ++tr << "#" << overrider.spec_index << " "
1353 << spec_name(m, &overrider) << " -> ";
1354
1355 tr << "#" << overrider.next->spec_index << " "
1356 << spec_name(m, overrider.next);
1357 *overrider.info->next =
1358 reinterpret_cast<void (*)()>(overrider.next->pf);
1359 } else {
1360 tr << "none";
1361 }
1362
1363 tr << "\n";
1364 }
1365 }
1366
1367 ++tr << "Initializing v-tables at " << gv_iter << "\n";
1368
1369 for (auto& cls : classes) {
1370 *cls.static_vptr = gv_iter - cls.first_slot;
1371
1372 ++tr << rflush(4, gv_iter - gv_first) << " " << gv_iter << " vtbl for "
1373 << cls << " slots " << cls.first_slot << "-"
1374 << (cls.first_slot + cls.vtbl.size() - 1) << "\n";
1375 indent _(tr);
1376
1377 for (auto& entry : cls.vtbl) {
1378 ++tr << "method #" << entry.method_index << " ";
1379 auto& method = methods[entry.method_index];
1380
1381 if (method.arity() == 1) {
1382 auto spec = method.dispatch_table[entry.group_index];
1383 tr << "spec #" << spec->spec_index << "\n";
1384 indent _(tr);
1385 ++tr << type_name(method.info->method_type_id) << "\n";
1386 ++tr << spec_name(method, spec);
1387 BOOST_ASSERT(gv_iter + 1 <= gv_last);
1388 *gv_iter++ = spec->pf;
1389 } else {
1390 tr << "vp #" << entry.vp_index << " group #"
1391 << entry.group_index << "\n";
1392 indent _2(tr);
1393 ++tr << type_name(method.info->method_type_id);
1394 BOOST_ASSERT(gv_iter + 1 <= gv_last);
1395
1396 if (entry.vp_index == 0) {
1397 *gv_iter++ = std::uintptr_t(
1398 method.gv_dispatch_table + entry.group_index);
1399 } else {
1400 *gv_iter++ = entry.group_index;
1401 }
1402 }
1403
1404 tr << "\n";
1405 }
1406 }
1407
1408 ++tr << rflush(4, dispatch_data_size) << " " << gv_iter << " end\n";
1409
1410 if constexpr (has_vptr) {
1411 vptr::initialize(*this, options);
1412 }
1413
1414 new_dispatch_data.swap(dispatch_data);
1415 }
1416
1417 template<class... Policies>
1418 template<class... Options>
1419 void registry<Policies...>::compiler<Options...>::select_dominant_overriders(
1420 std::vector<overrider*>& candidates, std::size_t& pick,
1421 std::size_t& remaining) {
1422
1423 pick = 0;
1424 remaining = 0;
1425
1426 for (size_t i = 0; i < candidates.size(); ++i) {
1427 if (candidates[i]) {
1428 for (size_t j = i + 1; j < candidates.size(); ++j) {
1429 if (candidates[j]) {
1430 if (is_more_specific(candidates[i], candidates[j])) {
1431 candidates[j] = nullptr;
1432 } else if (is_more_specific(candidates[j], candidates[i])) {
1433 candidates[i] = nullptr;
1434 break;
1435 }
1436 }
1437 }
1438 }
1439
1440 if (candidates[i]) {
1441 pick = i;
1442 ++remaining;
1443 }
1444 }
1445
1446 if (remaining <= 1) {
1447 return;
1448 }
1449
1450 if constexpr (has_option<n2216>) {
1451 if (!candidates[pick]->covariant_return_type) {
1452 return;
1453 }
1454
1455 remaining = 0;
1456
1457 for (size_t i = 0; i < candidates.size(); ++i) {
1458 if (candidates[i]) {
1459 for (size_t j = i + 1; j < candidates.size(); ++j) {
1460 if (candidates[j]) {
1461 BOOST_ASSERT(candidates[i] != candidates[j]);
1462
1463 if (candidates[i]->covariant_return_type->is_base_of(
1464 candidates[j]->covariant_return_type)) {
1465 candidates[i] = nullptr;
1466 } else if (candidates[j]
1467 ->covariant_return_type->is_base_of(
1468 candidates[i]
1469 ->covariant_return_type)) {
1470 candidates[j] = nullptr;
1471 }
1472 }
1473 }
1474 }
1475
1476 if (candidates[i]) {
1477 pick = i;
1478 ++remaining;
1479 }
1480 }
1481 }
1482 }
1483
1484 template<class... Policies>
1485 template<class... Options>
1486 auto registry<Policies...>::compiler<Options...>::is_more_specific(
1487 const overrider* a, const overrider* b) -> bool {
1488 bool result = false;
1489
1490 auto a_iter = a->vp.begin(), a_last = a->vp.end(), b_iter = b->vp.begin();
1491
1492 for (; a_iter != a_last; ++a_iter, ++b_iter) {
1493 if (*a_iter != *b_iter) {
1494 if ((*b_iter)->transitive_derived.find(*a_iter) !=
1495 (*b_iter)->transitive_derived.end()) {
1496 result = true;
1497 } else if (
1498 (*a_iter)->transitive_derived.find(*b_iter) !=
1499 (*a_iter)->transitive_derived.end()) {
1500 return false;
1501 }
1502 }
1503 }
1504
1505 return result;
1506 }
1507
1508 template<class... Policies>
1509 template<class... Options>
1510 auto registry<Policies...>::compiler<Options...>::is_base(
1511 const overrider* a, const overrider* b) -> bool {
1512 bool result = false;
1513
1514 auto a_iter = a->vp.begin(), a_last = a->vp.end(), b_iter = b->vp.begin();
1515
1516 for (; a_iter != a_last; ++a_iter, ++b_iter) {
1517 if (*a_iter != *b_iter) {
1518 if ((*a_iter)->transitive_derived.find(*b_iter) ==
1519 (*a_iter)->transitive_derived.end()) {
1520 return false;
1521 } else {
1522 result = true;
1523 }
1524 }
1525 }
1526
1527 return result;
1528 }
1529
1530 template<class... Policies>
1531 template<class... Options>
1532 void registry<Policies...>::compiler<Options...>::print(
1533 const method_report& r) const {
1534 ++tr;
1535
1536 if (r.cells) {
1537
1538 ++tr << r.cells << " dispatch table cells, ";
1539 }
1540
1541 tr << r.not_implemented << " not implemented, " << r.ambiguous
1542 << " ambiguous\n";
1543 }
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611 template<class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, class... Options>
1612 inline auto initialize(Options&&... options) {
1613 if (detail::odr_check<Registry>::count > 1) {
1614
1615
1616
1617 if constexpr (Registry::has_error_handler) {
1618 Registry::error_handler::error(odr_violation());
1619 }
1620
1621 std::abort();
1622 }
1623
1624 typename Registry::template compiler<Options...> comp(
1625 std::forward<Options>(options)...);
1626 comp.initialize();
1627
1628 return comp;
1629 }
1630
1631 namespace detail {
1632
1633 template<typename, class Policy, class... Options>
1634 struct has_finalize_aux : std::false_type {};
1635
1636 template<class Policy, class... Options>
1637 struct has_finalize_aux<
1638 std::void_t<decltype(Policy::finalize(
1639 std::declval<std::tuple<Options...>>()))>,
1640 Policy, Options...> : std::true_type {};
1641
1642 }
1643
1644 template<class... Policies>
1645 template<class... Options>
1646 auto registry<Policies...>::finalize(Options... opts) -> void {
1647 std::tuple<Options...> options(opts...);
1648 mp11::mp_for_each<policy_list>([&options](auto policy) {
1649 using fn = typename decltype(policy)::template fn<registry>;
1650 if constexpr (detail::has_finalize_aux<void, fn, Options...>::value) {
1651 fn::finalize(options);
1652 }
1653 });
1654
1655 dispatch_data.clear();
1656 initialized = false;
1657 }
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672 template<class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, class... Options>
1673 inline auto finalize(Options&&... opts) -> void {
1674 Registry::finalize(std::forward<Options>(opts)...);
1675 }
1676
1677 }
1678
1679 #ifdef _MSC_VER
1680 #pragma warning(pop)
1681 #endif
1682
1683 #endif