717 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			717 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
| This file is part of Telegram Desktop,
 | |
| the official desktop version of Telegram messaging app, see https://telegram.org
 | |
| 
 | |
| Telegram Desktop is free software: you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation, either version 3 of the License, or
 | |
| (at your option) any later version.
 | |
| 
 | |
| It is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| In addition, as a special exception, the copyright holders give permission
 | |
| to link the code of portions of this program with the OpenSSL library.
 | |
| 
 | |
| Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
 | |
| Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 | |
| */
 | |
| #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() {
 | |
| 		t_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() {
 | |
| 			t_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
 | 
