I have written a simple Component Based Architecture API in C++. It verges on Event-Drive Architecture.
The objective is simply to create a Component Based Architecture API that follows C++11 practices (with some C++14) and doesn't use any bad C++ practices or no-nos.
The API can be found including a working example. Its very simple and easy to comprehend in my opinion. The API only has 5 main classes.
I'm hoping people could be kind enough to review the code. I am looking for criticism and advice regarding:
- Any bad c++ practices and no-no's
- The API relies on void* as a way to pass generic structures. Is there a better alternative/pattern?
- How to ensure classes that inherit from IApp can only be instantiated through IApp::create<type>() and not directly through the constructor
- Is it thread-safe? What ways could this be optimised?
- I recently ran into the 'static initialisation order fiasco', are there any other problems I have implemented that I don't know about?
*Note: Some code is cross-platform but the main objective of the API isn't to be cross-platform. The Win32 example components are (obviously) quite coupled to OS. The objective of the API isn't to be released or be an alternative to existing API's just an exercise to use C++, improve coding skills and fulfil a code boner I've always had for CBA.
The main structure is like so:
// Event Delegate //
#pragma message("TODO: Similar to WinForms passing event parameters as a void pointer. Any ideas of a better solution that maintains polymorphism?")
typedef std::weak_ptr<void> EventArgs;
const EventArgs NULL_ARGS;
class EventDelegate
{
public:
typedef std::function<Status(EventArgs)> EDelegate;
EventDelegate(EDelegate delegate, GUID gUidContext);
bool operator== (const EventDelegate& other) const;
bool operator!= (const EventDelegate& other) const;
Status operator()(const EventArgs& evtArgs) const;
private:
GUID gUidContext;
EDelegate delegate;
};
// Component //
class Component
{
public:
GUID gUid;
template<typename T>
static std::unique_ptr<T> create(std::weak_ptr<IApp> app)
{
return std::unique_ptr<T>(new T(app));
}
virtual ~Component();
bool operator< (const Component& other) const;
template<typename T>
bool addComponent()
{
std::unique_ptr<T> cmp = Component::create<T>(app);
auto res = components.emplace(cmp->gUid, std::move(cmp));
return res.second;
}
protected:
std::weak_ptr<IApp> app;
std::unordered_map<GUID, std::unique_ptr<Component>> components;
Component(std::weak_ptr<IApp> app);
Component();
virtual Status init(const EventArgs& evtArgs) = 0;
virtual Status terminate(const EventArgs& evtArgs) = 0;
virtual Status registerEvents() = 0;
};
// IApp //
class IApp : protected Component
{
public:
static Status S_APP_EXIT;
static Status S_UNREGISTERED_EVT;
static const int EVT_MOUSE = 50000;
static const int EVT_KEY = 50001;
static const int EVT_INIT = 50002;
static const int EVT_TERMINATE = 50003;
static const int EVT_EXIT = 50004;
template<typename T>
static std::shared_ptr<T> create()
{
std::shared_ptr<T> app(new T);
app->app = app;
return app;
}
Status exec();
Status registerForEvent(const int& eventId, EventDelegate delegate);
Status unregisterForEvent(const int& eventId, EventDelegate delegate);
protected:
static std::unordered_multimap<int, EventDelegate> evtRegistry;
IApp();
virtual ~IApp();
virtual Status update() = 0;
static Status eventHandler(const int& evtId, const EventArgs& evtArgs);
private:
};