| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #pragma once
- namespace base {
- template <typename Object, typename ParentObject = void>
- class virtual_object;
- template <typename ConcreteMethod, typename ReturnType, typename ...Args>
- class virtual_method;
- template <typename ConcreteMethod, typename BaseMethod>
- class virtual_override;
- namespace virtual_methods {
- struct child_entry;
- using is_parent_check = bool(*)(const child_entry &possible_parent);
- struct child_entry {
- is_parent_check check_is_parent;
- int *table_index;
- };
- using child_entries = std::vector<child_entry>;
- // Recursive method to find if some class is a child of some other class.
- template <typename ConcreteObject>
- struct is_parent {
- static inline bool check(const child_entry &possible_parent) {
- // Generate a good error message if ConcreteObject is not a child of virtual_object<>.
- using all_objects_must_derive_virtual_object = typename ConcreteObject::virtual_object_parent;
- using ConcreteObjectParent = all_objects_must_derive_virtual_object;
- return (possible_parent.check_is_parent == &is_parent<ConcreteObject>::check)
- || is_parent<ConcreteObjectParent>::check(possible_parent);
- }
- };
- template <>
- struct is_parent<void> {
- static inline bool check(const child_entry &possible_parent) {
- return (possible_parent.check_is_parent == &is_parent<void>::check);
- }
- };
- // Just force the compiler not to optimize away the object that "enforce" points at.
- inline void dont_optimize_away(void *enforce) {
- static volatile void *result = nullptr;
- if (result) {
- result = enforce;
- }
- }
- template <typename Type, Type Value>
- struct dont_optimize_away_struct {
- };
- inline bool first_dispatch_fired(bool did_fire = false) {
- static bool fired = false;
- if (did_fire) {
- fired = true;
- }
- return fired;
- }
- template <typename Object, void (*Creator)(const child_entry &)>
- class object_registrator {
- public:
- inline object_registrator() {
- Assert(!first_dispatch_fired());
- Creator(child_entry {
- &is_parent<Object>::check,
- &_index,
- });
- }
- static inline int &Index() {
- return _index;
- }
- private:
- static int _index;
- };
- template <typename Object, void (*Creator)(const child_entry &)>
- int object_registrator<Object, Creator>::_index = -1;
- class object_base {
- protected:
- virtual ~object_base() = default;
- };
- template <typename ...ConcreteArgs>
- struct multi_index_collector;
- template <int M, typename ...ConcreteArgs>
- struct override_key_collector_helper;
- template <typename Call, typename ...Args>
- struct table_fill_entry_helper;
- template <typename ...Args>
- struct table_count_size;
- } // namespace virtual_methods
- // This should be a base class for every child object in your hierarchy.
- // It registers this child in the root virtual_objects classes list.
- // Also it holds its own index in the classes list that is used for fast
- // invoking of methods from the virtual tables in different virtual_methods.
- template <typename Object, typename ParentObject>
- class virtual_object : public ParentObject {
- protected:
- virtual ~virtual_object() {
- virtual_methods::dont_optimize_away(&_virtual_object_registrator);
- }
- private:
- using virtual_object_parent = ParentObject;
- friend struct virtual_methods::is_parent<Object>;
- template <typename ...Args>
- friend struct virtual_methods::multi_index_collector;
- template <int M, typename ...ConcreteArgs>
- friend struct virtual_methods::override_key_collector_helper;
- template <typename OtherObject, typename OtherParentObject>
- friend class virtual_object;
- template <typename BaseMethod, typename ReturnType, typename ...Args>
- friend class virtual_method;
- static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) {
- return ParentObject::virtual_object_register_child(entry);
- }
- using virtual_object_registrator = virtual_methods::object_registrator<Object, &virtual_object::virtual_object_register_child>;
- static virtual_object_registrator _virtual_object_registrator;
- using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_object_registrator*, &_virtual_object_registrator>;
- static inline int &virtual_object_child_index_static() {
- return virtual_object_registrator::Index();
- }
- int &virtual_object_child_index() override {
- return virtual_object_child_index_static();
- }
- };
- template <typename Object, typename ParentObject>
- typename virtual_object<Object, ParentObject>::virtual_object_registrator virtual_object<Object, ParentObject>::_virtual_object_registrator = {};
- // This should be a base class for the root of the whole hierarchy.
- // It holds the table of all child classes in a list.
- // This list is used by virtual_methods to generate virtual table.
- template <typename Object>
- class virtual_object<Object, void> : public virtual_methods::object_base {
- protected:
- virtual ~virtual_object() {
- virtual_methods::dont_optimize_away(&_virtual_object_registrator);
- }
- private:
- using virtual_object_parent = void;
- friend struct virtual_methods::is_parent<Object>;
- template <typename ...Args>
- friend struct virtual_methods::table_count_size;
- template <typename ...Args>
- friend struct virtual_methods::multi_index_collector;
- template <int M, typename ...ConcreteArgs>
- friend struct virtual_methods::override_key_collector_helper;
- template <typename Call, typename ...Args>
- friend struct virtual_methods::table_fill_entry_helper;
- template <typename OtherObject, typename OtherParentObject>
- friend class virtual_object;
- template <typename BaseMethod, typename ReturnType, typename ...Args>
- friend class virtual_method;
- static inline virtual_methods::child_entries &virtual_object_get_child_entries() {
- static virtual_methods::child_entries entries;
- return entries;
- }
- // Registers a new child class.
- // After that on the next call to virtual_method::virtual_method_prepare_table() will
- // generate a new virtual table for that virtual method.
- static inline void virtual_object_register_child(const virtual_methods::child_entry &entry) {
- auto &entries = virtual_object_get_child_entries();
- for (auto i = entries.begin(), e = entries.end(); i != e; ++i) {
- if (entry.check_is_parent(*i)) {
- *entry.table_index = (i - entries.begin());
- i = entries.insert(i, entry);
- for (++i, e = entries.end(); i != e; ++i) {
- ++*(i->table_index);
- }
- return;
- }
- }
- *entry.table_index = entries.size();
- entries.push_back(entry);
- }
- using virtual_object_registrator = virtual_methods::object_registrator<Object, &virtual_object::virtual_object_register_child>;
- static virtual_object_registrator _virtual_object_registrator;
- using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_object_registrator*, &_virtual_object_registrator>;
- static inline int &virtual_object_child_index_static() {
- return virtual_object_registrator::Index();
- }
- virtual int &virtual_object_child_index() {
- return virtual_object_child_index_static();
- }
- };
- template <typename Object>
- typename virtual_object<Object, void>::virtual_object_registrator virtual_object<Object, void>::_virtual_object_registrator = {};
- namespace virtual_methods {
- template <typename Arg>
- struct is_virtual_argument : public std::integral_constant<bool,
- base::type_traits<Arg>::is_pointer::value
- ? std::is_base_of<object_base, typename base::type_traits<Arg>::pointed_type>::value
- : false> {
- };
- template <int N, int Instance>
- class multi_int_wrap {
- public:
- inline multi_int_wrap(int *indices) : _indices(indices) {
- }
- inline multi_int_wrap<N - 1, Instance> subindex() const {
- static_assert(N > 0, "Wrong multi_int_wrap created!");
- return multi_int_wrap<N - 1, Instance>(_indices + 1);
- }
- inline int ¤t() const {
- return *_indices;
- }
- private:
- int *_indices;
- };
- template <int Instance>
- class multi_int_wrap<0, Instance> {
- public:
- inline multi_int_wrap(int *indices) {
- }
- inline int current() const {
- return 1;
- }
- };
- template <int N>
- using multi_index_wrap = multi_int_wrap<N, 0>;
- template <int N>
- using multi_size_wrap = multi_int_wrap<N, 1>;
- template <typename ConcreteArg, typename ...ConcreteArgs>
- struct multi_index_collector<ConcreteArg, ConcreteArgs...> {
- static constexpr int N = sizeof...(ConcreteArgs) + 1;
- static inline void call(multi_index_wrap<N> indices, ConcreteArg arg, ConcreteArgs... args) {
- indices.current() = computeIndex(is_virtual_argument<ConcreteArg>(), arg);
- multi_index_collector<ConcreteArgs...>::call(indices.subindex(), args...);
- }
- static inline int computeIndex(std::integral_constant<bool, false>, ConcreteArg arg) {
- return 0;
- }
- static inline int computeIndex(std::integral_constant<bool, true>, ConcreteArg arg) {
- return arg->virtual_object_child_index();
- }
- };
- template <>
- struct multi_index_collector<> {
- static inline void call(multi_index_wrap<0> indices) {
- }
- };
- template <int N>
- class override_key;
- template <int N, int Instance>
- class multi_int {
- public:
- inline multi_int_wrap<N, Instance> data_wrap() {
- return multi_int_wrap<N, Instance>(_indices);
- }
- template <typename ...ConcreteArgs>
- static inline multi_int<N, Instance> collect(ConcreteArgs... args) {
- multi_int<N, Instance> result;
- multi_index_collector<ConcreteArgs...>::call(result.data_wrap(), args...);
- return result;
- }
- inline void reset() {
- memset(_indices, 0, sizeof(_indices));
- }
- inline int value(int index) const {
- return _indices[index];
- }
- inline void copy(multi_int_wrap<N, Instance> other) {
- memcpy(_indices, &other.current(), sizeof(_indices));
- }
- private:
- int _indices[N] = { 0 };
- friend class override_key<N>;
- };
- template <int N>
- using multi_index = multi_int<N, 0>;
- template <int N>
- using multi_size = multi_int<N, 1>;
- template <typename Call, int N>
- class table_data_wrap {
- public:
- inline table_data_wrap(Call *data, multi_size_wrap<N> size) : _data(data), _size(size) {
- }
- inline table_data_wrap<Call, N - 1> operator[](int index) const {
- return table_data_wrap<Call, N - 1>(_data + index * _size.subindex().current(), _size.subindex());
- }
- inline Call &operator[](multi_index_wrap<N> index) const {
- return (*this)[index.current()][index.subindex()];
- }
- inline int size() const {
- return count_size(std::integral_constant<int,N>());
- }
- private:
- template <int M>
- inline int count_size(std::integral_constant<int,M>) const {
- return _size.current() / _size.subindex().current();
- }
- inline int count_size(std::integral_constant<int,1>) const {
- return _size.current();
- }
- Call *_data;
- multi_size_wrap<N> _size;
- };
- template <typename Call>
- class table_data_wrap<Call, 0> {
- public:
- inline table_data_wrap(Call *data, multi_size_wrap<0> size) : _data(data) {
- }
- inline Call &operator[](multi_index_wrap<0> index) const {
- return *_data;
- }
- private:
- Call *_data;
- };
- template <typename Call, int N>
- class table_data_wrap;
- template <typename Arg, typename ...Args>
- struct table_count_size<Arg, Args...> {
- static constexpr int N = sizeof...(Args) + 1;
- static inline void call(multi_size_wrap<N> index) {
- auto subindex = index.subindex();
- table_count_size<Args...>::call(subindex);
- index.current() = count(is_virtual_argument<Arg>()) * subindex.current();
- }
- static inline int count(std::integral_constant<bool, false>) {
- return 1;
- }
- static inline int count(std::integral_constant<bool, true>) {
- return base::type_traits<Arg>::pointed_type::virtual_object_get_child_entries().size();
- }
- };
- template <>
- struct table_count_size<> {
- static inline void call(multi_size_wrap<0> index) {
- }
- };
- template <typename Call, int N>
- class table_data {
- public:
- inline table_data_wrap<Call, N> data_wrap() {
- return table_data_wrap<Call, N>(_data.data(), _size.data_wrap());
- }
- inline Call &operator[](multi_index<N> index) {
- int flat_index = 0;
- for (int i = 0; i != N - 1; ++i) {
- flat_index += _size.value(i + 1) * index.value(i);
- }
- flat_index += index.value(N - 1);
- return _data[flat_index];
- }
- template <typename ...Args>
- inline bool changed() {
- if (!_data.empty()) {
- return false;
- }
- multi_size<N> size;
- table_count_size<Args...>::call(size.data_wrap());
- _size = size;
- _data.resize(_size.value(0), nullptr);
- return true;
- }
- private:
- std::vector<Call> _data;
- multi_size<N> _size;
- };
- template <typename Call>
- class table_data<Call, 0> {
- public:
- inline table_data_wrap<Call, 0> data_wrap() {
- return table_data_wrap<Call, 0>(&_call, multi_size_wrap<0>(nullptr));
- }
- inline Call &operator[](multi_index<0> index) {
- return _call;
- }
- inline bool changed() const {
- return false;
- }
- private:
- Call _call = nullptr;
- };
- template <typename Call, typename ...Args>
- struct table_fill_entry_helper;
- template <typename Call, typename Arg, typename ...Args>
- struct table_fill_entry_helper<Call, Arg, Args...> {
- static constexpr int N = sizeof...(Args) + 1;
- static inline bool call(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
- auto start = index.current();
- for (auto i = start, count = table.size(); i != count; ++i) {
- auto foundGoodType = good(is_virtual_argument<Arg>(), start, index.current());
- if (foundGoodType) {
- index.current() = i;
- if (table_fill_entry_helper<Call, Args...>::call(table[i], index.subindex(), fill)) {
- return true;
- }
- }
- }
- index.current() = start;
- return false;
- }
- static inline bool good(std::integral_constant<bool,false>, int start, int current) {
- return (start == current);
- }
- static inline bool good(std::integral_constant<bool,true>, int start, int current) {
- using BaseObject = typename base::type_traits<Arg>::pointed_type;
- auto &entries = BaseObject::virtual_object_get_child_entries();
- return (start == current) || entries[start].check_is_parent(entries[current]);
- }
- };
- template <typename Call>
- struct table_fill_entry_helper<Call> {
- static inline bool call(table_data_wrap<Call, 0> table, multi_index_wrap<0> index, Call &fill) {
- if (auto overrideMethod = table[index]) {
- fill = overrideMethod;
- return true;
- }
- return false;
- }
- };
- template <typename Call, int N>
- struct table_fill_entry;
- template <typename ReturnType, int N, typename BaseMethod, typename ...Args>
- struct table_fill_entry<ReturnType(*)(BaseMethod*, Args...), N> {
- using Call = ReturnType(*)(BaseMethod*, Args...);
- static inline void call(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
- table_fill_entry_helper<Call, Args...>::call(table, index, fill);
- }
- };
- template <typename Call, int N>
- inline void fill_entry(table_data_wrap<Call, N> table, multi_index_wrap<N> index, Call &fill) {
- return virtual_methods::table_fill_entry<Call, N>::call(table, index, fill);
- }
- template <int M, typename ...ConcreteArgs>
- struct override_key_collector_helper;
- template <int M, typename ConcreteArg, typename ...ConcreteArgs>
- struct override_key_collector_helper<M, ConcreteArg, ConcreteArgs...> {
- static inline void call(int **indices) {
- setValue(is_virtual_argument<ConcreteArg>(), indices);
- override_key_collector_helper<M + 1, ConcreteArgs...>::call(indices);
- }
- static inline void setValue(std::integral_constant<bool,false>, int **indices) {
- indices[M] = nullptr;
- }
- static inline void setValue(std::integral_constant<bool,true>, int **indices) {
- using ConcreteObject = typename base::type_traits<ConcreteArg>::pointed_type;
- using IsParentCheckStruct = is_parent<ConcreteObject>;
- using IsParentCheckPointer = decltype(&IsParentCheckStruct::check);
- using override_key_collector_dont_optimize_away = dont_optimize_away_struct<IsParentCheckPointer, &IsParentCheckStruct::check>;
- override_key_collector_dont_optimize_away dont_optimize_away_object;
- (void)dont_optimize_away_object;
- // Check that is_parent<> can be instantiated.
- // So every ConcreteObject is a valid child of virtual_object<>.
- dont_optimize_away(reinterpret_cast<void*>(&IsParentCheckStruct::check));
- indices[M] = &ConcreteObject::virtual_object_child_index_static();
- }
- };
- template <int M>
- struct override_key_collector_helper<M> {
- static inline void call(int **indices) {
- }
- };
- template <typename CallSignature>
- struct override_key_collector;
- template <typename ReturnType, typename BaseMethod, typename ...ConcreteArgs>
- struct override_key_collector<ReturnType(*)(BaseMethod, ConcreteArgs...)> {
- static inline void call(int **indices) {
- override_key_collector_helper<0, ConcreteArgs...>::call(indices);
- }
- };
- template <int N>
- class override_key {
- public:
- inline multi_index<N> value() const {
- multi_index<N> result;
- for (int i = 0; i != N; ++i) {
- auto pointer = _indices[i];
- result._indices[i] = (pointer ? *pointer : 0);
- }
- return result;
- }
- friend inline bool operator<(const override_key &k1, const override_key &k2) {
- for (int i = 0; i != N; ++i) {
- auto pointer1 = k1._indices[i], pointer2 = k2._indices[i];
- if (pointer1 < pointer2) {
- return true;
- } else if (pointer1 > pointer2) {
- return false;
- }
- }
- return false;
- }
- template <typename CallSignature>
- inline void collect() {
- override_key_collector<CallSignature>::call(_indices);
- }
- private:
- int *_indices[N];
- };
- template <typename BaseMethod, typename ConcreteMethod, typename CallSignature, typename ...Args>
- struct static_cast_helper;
- template <typename BaseMethod, typename ConcreteMethod, typename ReturnType, typename ...ConcreteArgs, typename ...Args>
- struct static_cast_helper<BaseMethod, ConcreteMethod, ReturnType(*)(BaseMethod *, ConcreteArgs...), Args...> {
- static inline ReturnType call(BaseMethod *context, Args ...args) {
- return ConcreteMethod::call(context, static_cast<ConcreteArgs>(args)...);
- }
- };
- } // namespace virtual_methods
- // This is a base class for all your virtual methods.
- // It dispatches a call to one of the registered virtual_overrides
- // or calls the fallback method of the BaseMethod class.
- template <typename BaseMethod, typename ReturnType, typename ...Args>
- class virtual_method {
- static constexpr int N = sizeof...(Args);
- using virtual_method_call = ReturnType(*)(BaseMethod *context, Args... args);
- public:
- inline ReturnType call(Args... args) {
- auto context = static_cast<BaseMethod*>(this);
- auto index = virtual_methods::multi_index<N>::collect(args...);
- auto &table = virtual_method_prepare_table();
- auto &entry = table[index];
- if (!entry) {
- virtual_methods::fill_entry(table.data_wrap(), index.data_wrap(), entry);
- if (!entry) {
- entry = &virtual_method::virtual_method_base_instance;
- }
- }
- return (*entry)(context, args...);
- }
- private:
- // This map of methods contains only the original registered overrides.
- using virtual_method_override_key = virtual_methods::override_key<N>;
- using virtual_method_override_map = std::map<virtual_method_override_key, virtual_method_call>;
- static inline virtual_method_override_map &virtual_method_get_override_map() {
- static virtual_method_override_map override_map;
- return override_map;
- }
- // This method generates and returns a virtual table which holds a method
- // for any child in the hierarchy or nullptr if none of the virtual_overrides fit.
- using virtual_method_table_data = virtual_methods::table_data<virtual_method_call, N>;
- static inline virtual_method_table_data &virtual_method_get_table_data() {
- static virtual_method_table_data virtual_table;
- return virtual_table;
- }
- static inline virtual_method_table_data &virtual_method_prepare_table() {
- auto &virtual_table = virtual_method_get_table_data();
- if (virtual_table.template changed<Args...>()) {
- virtual_methods::first_dispatch_fired(true);
- // The class hierarchy has changed - we need to generate the virtual table once again.
- // All other handlers will be placed if they're called.
- for (auto &i : virtual_method_get_override_map()) {
- virtual_table[i.first.value()] = i.second;
- }
- }
- return virtual_table;
- }
- static ReturnType virtual_method_base_instance(BaseMethod *context, Args... args) {
- return BaseMethod::default_call(context, args...);
- }
- template <typename ConcreteMethod>
- static ReturnType virtual_method_override_instance(BaseMethod *context, Args... args) {
- return virtual_methods::static_cast_helper<BaseMethod, ConcreteMethod, decltype(&ConcreteMethod::call), Args...>::call(context, args...);
- }
- template <typename ConcreteMethod>
- static inline void virtual_method_register_override() {
- auto call = &virtual_method_override_instance<ConcreteMethod>;
- virtual_methods::override_key<N> key;
- key.template collect<decltype(&ConcreteMethod::call)>();
- virtual_method_get_override_map()[key] = call;
- }
- template <typename ConcreteMethod, typename OtherBaseMethod>
- friend class virtual_override;
- };
- template <typename ConcreteMethod, typename BaseMethod>
- class virtual_override {
- protected:
- virtual ~virtual_override() {
- virtual_methods::dont_optimize_away(&_virtual_override_registrator);
- }
- private:
- class virtual_override_registrator {
- public:
- inline virtual_override_registrator() {
- Assert(!virtual_methods::first_dispatch_fired());
- BaseMethod::template virtual_method_register_override<ConcreteMethod>();
- }
- };
- static virtual_override_registrator _virtual_override_registrator;
- using virtual_override_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct<virtual_override_registrator*, &_virtual_override_registrator>;
- };
- template <typename ConcreteMethod, typename BaseMethod>
- typename virtual_override<ConcreteMethod, BaseMethod>::virtual_override_registrator virtual_override<ConcreteMethod, BaseMethod>::_virtual_override_registrator = {};
- } // namespace base
|