Last active
January 14, 2017 23:43
-
-
Save Bueddl/6a5fa8da57909d2e4bc85f5509273024 to your computer and use it in GitHub Desktop.
Runtime Type Information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <exception> | |
#include <iostream> | |
#include <unordered_map> | |
class object_base; | |
struct rt_type_info | |
{ | |
const char *name; | |
const rt_type_info *base; | |
object_base* (*create_fn)(); | |
void (*delete_fn)(object_base *); | |
}; | |
class object_base | |
{ | |
public: | |
virtual ~object_base() | |
{} | |
virtual const rt_type_info *type_info() const = 0; | |
}; | |
template<class T, class B = T> | |
class rt_type_info_enabled : virtual public rt_type_info_enabled<T>, virtual public rt_type_info_enabled<B>, public B | |
{ | |
const rt_type_info *type_info() const override | |
{ | |
return rt_type_info_enabled<T>::type_info(); | |
} | |
}; | |
template<class T, class B> | |
class type_info_factory; | |
template<class T> | |
class rt_type_info_enabled<T, T> : virtual public object_base | |
{ | |
friend class rt_type_info_init; | |
template<class fT, class fB> | |
friend class type_info_factory; | |
public: | |
const rt_type_info *type_info() const override | |
{ | |
return _rtti; | |
} | |
static const rt_type_info *get() | |
{ | |
return _rtti; | |
} | |
private: | |
static void set(rt_type_info *type_info) | |
{ | |
_rtti = type_info; | |
} | |
static rt_type_info *_rtti; | |
}; | |
template<class T> | |
rt_type_info *rt_type_info_enabled<T>::_rtti = nullptr; | |
template<class C, class T> | |
bool instance_of(T *object) | |
{ | |
return rt_type_info_enabled<C>::get() == object->type_info(); | |
} | |
template<class B, class T> | |
bool derived_by(T *object) | |
{ | |
for(const rt_type_info *rtti = object->type_info(); rtti; rtti = rtti->base) | |
if (rt_type_info_enabled<B>::get() == rtti) | |
return true; | |
return false; | |
} | |
template<class T, class B> | |
T *try_cast(B *object) | |
{ | |
if (!derived_by<T>(object)) | |
return nullptr; | |
return reinterpret_cast<T *>(object); | |
}; | |
std::unordered_map<std::string, const rt_type_info*> type_info_storage; | |
template<class T> | |
object_base* default_creator() | |
{ | |
return new T; | |
} | |
void default_deleter(object_base *object) | |
{ | |
delete object; | |
} | |
template<class T, class B = T> | |
struct type_info_factory | |
{ | |
static void init(const char *name) | |
{ | |
static rt_type_info info{name, rt_type_info_enabled<B>::get(), default_creator<T>, default_deleter}; | |
rt_type_info_enabled<T>::set(&info); | |
type_info_storage.emplace(name, &info); | |
} | |
static void init_abstract(const char *name) | |
{ | |
static rt_type_info info{name, rt_type_info_enabled<B>::get(), nullptr, nullptr}; | |
rt_type_info_enabled<T>::set(&info); | |
type_info_storage.emplace(name, &info); | |
} | |
}; | |
template<class T> | |
struct type_info_factory<T, T> | |
{ | |
static void init(const char *name) | |
{ | |
static rt_type_info info{name, nullptr, default_creator<T>, default_deleter}; | |
rt_type_info_enabled<T>::set(&info); | |
type_info_storage.emplace(name, &info); | |
} | |
static void init_abstract(const char *name) | |
{ | |
static rt_type_info info{name, nullptr, nullptr, nullptr}; | |
rt_type_info_enabled<T>::set(&info); | |
type_info_storage.emplace(name, &info); | |
} | |
}; | |
class illegal_create_request_exception : public std::exception | |
{ | |
public: | |
illegal_create_request_exception(const rt_type_info *type_info) | |
: _type_info(type_info) | |
{} | |
const char *what() const noexcept override | |
{ | |
static std::string message = "cannot create instance of abstract class "; | |
message += _type_info->name; | |
return message.c_str(); | |
} | |
private: | |
const rt_type_info *_type_info; | |
}; | |
class unresolved_create_request_exception : public std::exception | |
{ | |
public: | |
unresolved_create_request_exception(const std::string class_name) | |
: _class_name(class_name) | |
{} | |
const char *what() const noexcept override | |
{ | |
static std::string message = "cannot create instance of unresolved class "; | |
message += _class_name; | |
return message.c_str(); | |
} | |
private: | |
const std::string _class_name; | |
}; | |
object_base* create_object(const rt_type_info *type_info) | |
{ | |
if (!type_info->create_fn) | |
throw illegal_create_request_exception(type_info); | |
return type_info->create_fn(); | |
} | |
object_base* create_object(const object_base *original) | |
{ | |
return create_object(original->type_info()); | |
} | |
object_base* create_object(const std::string &class_name) | |
{ | |
std::unordered_map<std::string, const rt_type_info*>::iterator it | |
= type_info_storage.find(class_name); | |
if (it == type_info_storage.end()) | |
throw unresolved_create_request_exception(class_name); | |
return create_object(it->second); | |
} | |
// -------------------------------------------------------- | |
class base : public rt_type_info_enabled<base> | |
{ | |
public: | |
void method_in_base() const | |
{ | |
std::cout << __PRETTY_FUNCTION__ << std::endl; | |
} | |
}; | |
class derived : public rt_type_info_enabled<derived, base> | |
{ | |
public: | |
void method_in_derived() const | |
{ | |
std::cout << __PRETTY_FUNCTION__ << std::endl; | |
} | |
}; | |
class other : public rt_type_info_enabled<other> | |
{ | |
public: | |
}; | |
// -------------------------------------------------------- | |
class type_info_init | |
{ | |
public: | |
type_info_init() | |
{ | |
type_info_factory<object_base>::init_abstract("object_base"); | |
type_info_factory<base, object_base>::init("base"); | |
type_info_factory<derived, base>::init("derived"); | |
type_info_factory<other, object_base>::init("other"); | |
} | |
} rtti_init; | |
int main() | |
{ | |
object_base *object = new derived; | |
std::cout << "object is of type " << object->type_info()->name << std::endl; | |
object_base *clone = create_object(object); | |
object->type_info()->delete_fn(object); | |
std::cout << "clone is of type " << clone->type_info()->name << std::endl; | |
derived *d; | |
if (d = try_cast<derived>(clone)) | |
d->method_in_derived(); | |
clone->type_info()->delete_fn(clone); | |
object_base *object_by_str = create_object("object_base"); | |
std::cout << "object_by_str is of type " << object_by_str->type_info()->name << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment