#include <iostream>
#include <set>
template <typename> class Registrable;
//
// class Registry
//
class Registry {
public:
template <typename> friend class Registrable;
template <typename T>
static T* Cast(void*);
private:
struct TagType {};
using Key = std::pair<TagType const*, void*>;
using Store = std::set<Key>;
template <typename T>
static void Register(Registrable<T>* t);
template <typename T>
static void Unregister(Registrable<T>* t);
static Store& Get();
}; // class Registry
template <typename T>
T* Registry::Cast(void* const pointer) {
TagType const* const tag = &Registrable<T>::Tag;
if (Get().count(std::make_pair(tag, pointer)) == 0) { return nullptr; }
return static_cast<T*>(reinterpret_cast<Registrable<T>*>(pointer));
}
template <typename T>
void Registry::Register(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().insert(std::make_pair(tag, pointer));
}
template <typename T>
void Registry::Unregister(Registrable<T>* t) {
TagType const* const tag = &T::Tag;
void* const pointer = reinterpret_cast<void*>(t);
Get().erase(std::make_pair(tag, pointer));
}
Registry::Store& Registry::Get() { static Store S; return S; }
//
// class Registrable
//
template <typename T>
class Registrable {
public:
static Registry::TagType const Tag;
Registrable();
~Registrable();
Registrable(Registrable&&) = default;
Registrable& operator=(Registrable&&) = default;
Registrable(Registrable const&) = default;
Registrable& operator=(Registrable const&) = default;
}; // class Registrable
template <typename T> Registry::TagType const Registrable<T>::Tag;
template <typename T>
Registrable<T>::Registrable() { Registry::Register(this); }
template <typename T>
Registrable<T>::~Registrable() { try { Registry::Register(this); } catch(...) {} }
//
// Example
//
class Example: public Registrable<Example> {};
int main() {
Example e;
void* p = &e;
void* q = &e + 1;
std::cout << &e << " " << p << " " << Registry::Cast<Example>(p) << "\n";
std::cout << (&e + 1) << " " << q << " " << Registry::Cast<Example>(q) << "\n";
return 0;
}