#ifndef APP_H #define APP_H #include "pch.hpp" #include "utils/Module/Module.h" #include "utils/Events/Event.h" namespace Archimedes { class App { public: App() { if(instance != nullptr) { std::cout << "App already exists\nThere can only be one!\n"; std::abort(); } instance = this; roInsert = runOrder.begin(); registerEvent(AnonymousEvent()); registerEvent(DoLoadModuleEvent()); registerEvent(DoUnloadModuleEvent()); registerEvent(LoadModuleEvent()); registerEvent(UnloadModuleEvent()); } virtual ~App() { unregisterEvent(AnonymousEvent()); unregisterEvent(DoLoadModuleEvent()); unregisterEvent(DoUnloadModuleEvent()); unregisterEvent(LoadModuleEvent()); unregisterEvent(UnloadModuleEvent()); for(std::string s : runOrder) { void* handle = modules[s]->getHandle(); delete modules[s]; if(handle) dlclose(handle); modules[s] = nullptr; } runOrder.clear(); } static App* Get() { return instance; } std::list getModuleNames() const { return runOrder; } virtual void handleArgs(const int&, char*[]) = 0; virtual void run() = 0; void end() { done = true; } bool isDone() const { return done; } void emitEvent(Event* e) { events.push_back(e); } unsigned int getEventType(std::string event) { return eventTypes[event]; } void registerEvent(std::string type) { //only add each type once if(eventTypes.find(type) == eventTypes.end()) eventTypes[type] = nextEventType++; } void unregisterEvent(std::string type) { //only erase registered types auto it = eventTypes.find(type); if(it != eventTypes.end()) eventTypes.erase(it); } private: std::list::iterator roInsert; inline static App* instance = nullptr; unsigned int nextEventType = 0; protected: bool done = false; std::unordered_map modules; std::unordered_map eventTypes; std::list events; std::list runOrder; std::list toClose; std::list> toOpen; virtual void stopModule(std::string lib) { //unload modules that depend on the one we are unloading for(std::string s : runOrder) { if(modules[s]->deps.find(lib) != modules[s]->deps.end()) { stopModule(s); } } toClose.push_back(lib); } virtual void startModule(std::variant m) { toOpen.push_back(m); } virtual bool onEvent(const Event& event) = 0; void handleEvents() { static bool handled; while(!events.empty()) { handled = false; for(auto it = runOrder.rbegin(); it != runOrder.rend(); it++) { handled = modules[*it]->onEvent(*events.front()); if(handled) { Event* e = events.front(); events.pop_front(); delete e; break; } } if(!handled) { if(this->onEvent(*events.front())) { Event* e = events.front(); events.pop_front(); delete e; } else { std::cout << "Error: Unhandled Event: " << (std::string) *events.front() << std::endl; } } } } virtual Module* dynamicLoad(std::string lib) { void* h = dlopen(lib.c_str(), RTLD_NOW); if(!h) { std::cout << "could not open lib at: \"" << lib.c_str() << "\"\nError: " << dlerror() << std::endl; return nullptr; } Module::create_t* create = (Module::create_t*) dlsym(h, "create"); char* err = dlerror(); if(err) { std::cout << "error finding create function in file: " << lib << std::endl; std::cout << "dlerror(): " << err << std::endl; return nullptr; } return create(Get(), h); } virtual Module* reload(std::string lib) { for(std::string s : runOrder) { if(s == lib) { return modules[lib]; } } return modules.find(lib) != modules.end() ? load(modules[lib]) : nullptr; } virtual Module* load(std::string moduleNameOrPath) { Module* m = dynamicLoad(moduleNameOrPath); //return m != nullptr ? load(m) : reload(moduleNameOrPath); return load(m); } virtual Module* load(Module* m) { if(!m) { return nullptr; } void* h = m->getHandle(); for(auto it = runOrder.begin(); it != runOrder.end(); it++) { if(*it == static_cast(*m)) { std::cout << "Module \"" << *it << "\" is already loaded!\n"; delete m; if(h) { dlclose(h); } return modules[*it]; } } if(modules.find(*m) == modules.end()) modules[*m] = m; for(auto it = runOrder.begin(); it != runOrder.end(); it++) { if(m->deps.find(*it) != m->deps.end()) { m->moduleInstances[*it] = modules[*it]; //modules should be located after their dependencies if(std::distance(roInsert, it) <= 0) { roInsert = ++it--; } } } //insert temporarily to avoid circular dependencies runOrder.insert(roInsert, *m); bool skip = false; for(auto it : m->deps) { for(auto s : runOrder) { if(s == it.first) { skip = true; m->moduleInstances[s] = modules[s]; } } if(skip) continue; if(std::holds_alternative(it.second)) m->moduleInstances[it.first] = load(std::get(it.second)); else m->moduleInstances[it.first] = load(std::get(it.second)); } //reinsert once final order has been reached runOrder.remove(*m); runOrder.insert(roInsert, *m); emitEvent(new LoadModuleEvent(*m)); return m; } virtual void unload(std::string name) { if(modules.find(name) == modules.end()) return; Module* m = modules[name]; void* h = m->getHandle(); modules[name] = nullptr; modules.erase(name); runOrder.remove(name); if(h) { //don't dlclose if other modules are still in runOrder! bool closable = true; for(auto s = runOrder.begin(); s != runOrder.end(); s++) { if(modules[*s]->getHandle() == h) { closable = false; } } delete m; if(closable) { dlclose(h); } } emitEvent(new UnloadModuleEvent(name)); } virtual void printHelp() = 0; }; } #endif