/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once namespace base { template class virtual_object; template class virtual_method; template 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; // Recursive method to find if some class is a child of some other class. template 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::check) || is_parent::check(possible_parent); } }; template <> struct is_parent { static inline bool check(const child_entry &possible_parent) { return (possible_parent.check_is_parent == &is_parent::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 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 class object_registrator { public: inline object_registrator() { Assert(!first_dispatch_fired()); Creator(child_entry { &is_parent::check, &_index, }); } static inline int &Index() { return _index; } private: static int _index; }; template int object_registrator::_index = -1; class object_base { protected: virtual ~object_base() = default; }; template struct multi_index_collector; template struct override_key_collector_helper; template struct table_fill_entry_helper; template 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 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; template friend struct virtual_methods::multi_index_collector; template friend struct virtual_methods::override_key_collector_helper; template friend class virtual_object; template 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; static virtual_object_registrator _virtual_object_registrator; using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; 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 virtual_object::virtual_object_registrator virtual_object::_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 class virtual_object : 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; template friend struct virtual_methods::table_count_size; template friend struct virtual_methods::multi_index_collector; template friend struct virtual_methods::override_key_collector_helper; template friend struct virtual_methods::table_fill_entry_helper; template friend class virtual_object; template 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; static virtual_object_registrator _virtual_object_registrator; using virtual_object_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; 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 virtual_object::virtual_object_registrator virtual_object::_virtual_object_registrator = {}; namespace virtual_methods { template struct is_virtual_argument : public std::integral_constant::is_pointer::value ? std::is_base_of::pointed_type>::value : false> { }; template class multi_int_wrap { public: inline multi_int_wrap(int *indices) : _indices(indices) { } inline multi_int_wrap subindex() const { static_assert(N > 0, "Wrong multi_int_wrap created!"); return multi_int_wrap(_indices + 1); } inline int ¤t() const { return *_indices; } private: int *_indices; }; template class multi_int_wrap<0, Instance> { public: inline multi_int_wrap(int *indices) { } inline int current() const { return 1; } }; template using multi_index_wrap = multi_int_wrap; template using multi_size_wrap = multi_int_wrap; template struct multi_index_collector { static constexpr int N = sizeof...(ConcreteArgs) + 1; static inline void call(multi_index_wrap indices, ConcreteArg arg, ConcreteArgs... args) { indices.current() = computeIndex(is_virtual_argument(), arg); multi_index_collector::call(indices.subindex(), args...); } static inline int computeIndex(std::integral_constant, ConcreteArg arg) { return 0; } static inline int computeIndex(std::integral_constant, ConcreteArg arg) { return arg->virtual_object_child_index(); } }; template <> struct multi_index_collector<> { static inline void call(multi_index_wrap<0> indices) { } }; template class override_key; template class multi_int { public: inline multi_int_wrap data_wrap() { return multi_int_wrap(_indices); } template static inline multi_int collect(ConcreteArgs... args) { multi_int result; multi_index_collector::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 other) { memcpy(_indices, &other.current(), sizeof(_indices)); } private: int _indices[N] = { 0 }; friend class override_key; }; template using multi_index = multi_int; template using multi_size = multi_int; template class table_data_wrap { public: inline table_data_wrap(Call *data, multi_size_wrap size) : _data(data), _size(size) { } inline table_data_wrap operator[](int index) const { return table_data_wrap(_data + index * _size.subindex().current(), _size.subindex()); } inline Call &operator[](multi_index_wrap index) const { return (*this)[index.current()][index.subindex()]; } inline int size() const { return count_size(std::integral_constant()); } private: template inline int count_size(std::integral_constant) const { return _size.current() / _size.subindex().current(); } inline int count_size(std::integral_constant) const { return _size.current(); } Call *_data; multi_size_wrap _size; }; template class table_data_wrap { 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 class table_data_wrap; template struct table_count_size { static constexpr int N = sizeof...(Args) + 1; static inline void call(multi_size_wrap index) { auto subindex = index.subindex(); table_count_size::call(subindex); index.current() = count(is_virtual_argument()) * subindex.current(); } static inline int count(std::integral_constant) { return 1; } static inline int count(std::integral_constant) { return base::type_traits::pointed_type::virtual_object_get_child_entries().size(); } }; template <> struct table_count_size<> { static inline void call(multi_size_wrap<0> index) { } }; template class table_data { public: inline table_data_wrap data_wrap() { return table_data_wrap(_data.data(), _size.data_wrap()); } inline Call &operator[](multi_index 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 inline bool changed() { if (!_data.empty()) { return false; } multi_size size; table_count_size::call(size.data_wrap()); _size = size; _data.resize(_size.value(0), nullptr); return true; } private: std::vector _data; multi_size _size; }; template class table_data { public: inline table_data_wrap data_wrap() { return table_data_wrap(&_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 struct table_fill_entry_helper; template struct table_fill_entry_helper { static constexpr int N = sizeof...(Args) + 1; static inline bool call(table_data_wrap table, multi_index_wrap index, Call &fill) { auto start = index.current(); for (auto i = start, count = table.size(); i != count; ++i) { auto foundGoodType = good(is_virtual_argument(), start, index.current()); if (foundGoodType) { index.current() = i; if (table_fill_entry_helper::call(table[i], index.subindex(), fill)) { return true; } } } index.current() = start; return false; } static inline bool good(std::integral_constant, int start, int current) { return (start == current); } static inline bool good(std::integral_constant, int start, int current) { using BaseObject = typename base::type_traits::pointed_type; auto &entries = BaseObject::virtual_object_get_child_entries(); return (start == current) || entries[start].check_is_parent(entries[current]); } }; template struct table_fill_entry_helper { static inline bool call(table_data_wrap table, multi_index_wrap<0> index, Call &fill) { if (auto overrideMethod = table[index]) { fill = overrideMethod; return true; } return false; } }; template struct table_fill_entry; template struct table_fill_entry { using Call = ReturnType(*)(BaseMethod*, Args...); static inline void call(table_data_wrap table, multi_index_wrap index, Call &fill) { table_fill_entry_helper::call(table, index, fill); } }; template inline void fill_entry(table_data_wrap table, multi_index_wrap index, Call &fill) { return virtual_methods::table_fill_entry::call(table, index, fill); } template struct override_key_collector_helper; template struct override_key_collector_helper { static inline void call(int **indices) { setValue(is_virtual_argument(), indices); override_key_collector_helper::call(indices); } static inline void setValue(std::integral_constant, int **indices) { indices[M] = nullptr; } static inline void setValue(std::integral_constant, int **indices) { using ConcreteObject = typename base::type_traits::pointed_type; using IsParentCheckStruct = is_parent; using IsParentCheckPointer = decltype(&IsParentCheckStruct::check); using override_key_collector_dont_optimize_away = dont_optimize_away_struct; 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(&IsParentCheckStruct::check)); indices[M] = &ConcreteObject::virtual_object_child_index_static(); } }; template struct override_key_collector_helper { static inline void call(int **indices) { } }; template struct override_key_collector; template struct override_key_collector { static inline void call(int **indices) { override_key_collector_helper<0, ConcreteArgs...>::call(indices); } }; template class override_key { public: inline multi_index value() const { multi_index 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 inline void collect() { override_key_collector::call(_indices); } private: int *_indices[N]; }; template struct static_cast_helper; template struct static_cast_helper { static inline ReturnType call(BaseMethod *context, Args ...args) { return ConcreteMethod::call(context, static_cast(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 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(this); auto index = virtual_methods::multi_index::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; using virtual_method_override_map = std::map; 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; 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()) { 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 static ReturnType virtual_method_override_instance(BaseMethod *context, Args... args) { return virtual_methods::static_cast_helper::call(context, args...); } template static inline void virtual_method_register_override() { auto call = &virtual_method_override_instance; virtual_methods::override_key key; key.template collect(); virtual_method_get_override_map()[key] = call; } template friend class virtual_override; }; template 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(); } }; static virtual_override_registrator _virtual_override_registrator; using virtual_override_dont_optimize_away_registrator = virtual_methods::dont_optimize_away_struct; }; template typename virtual_override::virtual_override_registrator virtual_override::_virtual_override_registrator = {}; } // namespace base