Created
March 16, 2019 23:26
-
-
Save lighth7015/f65135562dfc8ddcd96eced347966485 to your computer and use it in GitHub Desktop.
Auto-registration factory
This file contains hidden or 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 <cassert> | |
#include <functional> | |
#include <iostream> | |
#include <memory> | |
#include <unordered_map> | |
#include <utility> | |
/** | |
* Portions of this code adapted from https://stackoverflow.com/a/42191109/201787 | |
*/ | |
template<class T, class F> | |
class InvokeApplyOnConstruction | |
{ | |
public: | |
InvokeApplyOnConstruction() | |
{ | |
F::template Register<T>(); | |
} | |
}; | |
/** | |
* | |
*/ | |
template<class T, class F> | |
struct RegistrationInvoker { | |
static const InvokeApplyOnConstruction<T, F> _registrationInvoker; | |
}; | |
template<class T, class F> | |
const InvokeApplyOnConstruction<T, F> RegistrationInvoker<T, F>::_registrationInvoker | |
= InvokeApplyOnConstruction<T, F>(); | |
/** | |
* Classic Factory object allowing runtime registration | |
*/ | |
template<class T, class TPRoduct = std::unique_ptr<T>> | |
struct ModuleRegistry | |
{ | |
using Product = TPRoduct; | |
using Creator = std::function<Product()>; | |
static ModuleRegistry& Instance() // Singleton | |
{ | |
static ModuleRegistry instance; | |
return instance; | |
} | |
void publish( std::size_t id, Creator creator ) | |
{ | |
if ( _collection.find(id) == std::end(_collection) ) { | |
_collection[id] = std::move(creator); | |
} | |
} | |
void publish( std::size_t id, std::size_t type_id, Creator creator ) | |
{ | |
assert( _collection.find(type_id) == std::end( _collection )); | |
printf( "id: %lx, type: %lx\n", id, type_id ); | |
_mapping[id] = type_id; | |
_collection[type_id] = std::move(creator); | |
} | |
Product get( const std::size_t id ) const | |
{ | |
const auto entry = _collection.find(id); | |
assert( entry != std::end( _collection )); | |
} | |
private: | |
ModuleRegistry() = default; | |
ModuleRegistry(const ModuleRegistry&) = delete; | |
std::unordered_map<std::size_t, std::size_t> _mapping; | |
std::unordered_map<std::size_t, Creator> _collection; | |
}; | |
/** | |
* | |
*/ | |
class Connection | |
{ | |
public: | |
virtual void* get() const = 0; | |
virtual ~Connection() = default; | |
}; | |
class ModuleBase | |
{ | |
public: | |
virtual void get() const = 0; | |
virtual ~ModuleBase() = default; | |
}; | |
/** | |
* Invoked by the generic auto-registration classes. | |
*/ | |
struct RegisterConnectionInstance | |
{ | |
template<typename T> | |
static void Register() | |
{ | |
ModuleRegistry<Connection>::Instance() | |
.publish( T::id, [] { return std::make_unique<T>(); } ); | |
} | |
}; | |
struct RegisterModuleBaseInstance | |
{ | |
template<typename T> | |
static void Register() | |
{ | |
ModuleRegistry<ModuleBase>::Instance() | |
.publish( T::id, [] { return std::make_unique<T>(); }); | |
} | |
}; | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
template<class Derived> | |
class ConnectionFactory : RegistrationInvoker<Derived, RegisterConnectionInstance> | |
{ | |
public: | |
static const std::size_t id; | |
}; | |
template<class Derived> | |
class ModuleFactory : RegistrationInvoker<Derived, RegisterModuleBaseInstance> | |
{ | |
public: | |
static const std::size_t id; | |
}; | |
template<class Derived> | |
const std::size_t ConnectionFactory<Derived>::id = | |
reinterpret_cast<std::size_t>( &RegistrationInvoker<Derived, RegisterConnectionInstance>::_registrationInvoker ); | |
template<class Derived> | |
const std::size_t ModuleFactory<Derived>::id = | |
reinterpret_cast<std::size_t>( &RegistrationInvoker<Derived, RegisterModuleBaseInstance>::_registrationInvoker ); | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
// Derived types | |
//////////////////////////////////////////////////////////////////////////////////////////////// | |
class ScriptRuntime: public ModuleBase | |
, public ModuleFactory<ScriptRuntime> | |
{ | |
public: | |
ScriptRuntime() { | |
std::cout << "Init Scripting Environment" << std::endl; | |
} | |
void get() const override { | |
std::cout << "Return self." << std::endl; | |
} | |
}; | |
class PosixConnection: public Connection | |
, public ConnectionFactory<PosixConnection> | |
{ | |
public: | |
static const std::size_t type = Posix; | |
PosixConnection() { | |
std::cout << "Establish XCB connection" << std::endl; | |
} | |
void* get() const override { | |
std::cout << "XCB connection" << std::endl; | |
return nullptr; | |
} | |
}; | |
int main() | |
{ | |
const auto& factory = | |
ModuleRegistry<Connection>::Instance(); | |
// Shouldn't create three instances of a PosixConnection. | |
factory.get( PosixConnection::id ); | |
factory.get( PosixConnection::id ); | |
factory.get( PosixConnection::id ); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment