Currently I work on application which measures and registers time that I'm spending in particular application. Since I use various of operating systems (Windows at work, and Linux at home), application has to be available on multiple platforms.
Sometimes I don't want to be tracked (e.g. when the screen is locked), so I've defined collection of rules, when tracking should be suspended:
Here's a code:
And that's how the code above can be used:
I've created Base class, and two inherited classes (WithParameter and NoParameter). Both derived classes have static registrar object, which automatically adds instances of the classes to the collection.
Unfortunately, constructor parameters (if any), have to be compile time constants. But in my case, it's not a problem.
Now, we can e.g. enumerate our collection:
Full code with example can be found on my github's gist [1].
Links
[1] https://gist.github.com/loganek/c0c8dfb30cd72a5a1a47b3701b61d3da
Sometimes I don't want to be tracked (e.g. when the screen is locked), so I've defined collection of rules, when tracking should be suspended:
std::vector<std::shared_ptr<Rule>> rules; rules.push_back(std::make_shared<Rule1>()); rules.push_back(std::make_shared<Rule2>()); rules.push_back(std::make_shared<Rule3>()); rules.push_back(std::make_shared<Rule4>());However, some of the rules are OS-specific (e.g. Rule2 and Rule3 compile and work only in Windows, and Rule4 makes sense only when app is running on Linux). So what I did:
- Conditionally enabled files rule2.cc, rule3.cc and rule4.cc in the build system
if (WIN32) set (RULE_FILES rule2.cc rule3.cc) elseif (UNIX) set (RULE_FILES rule4.cc) endif (WIN32)
- Conditionally pushed instances of Rule2, Rule3 and Rule4 to the std::vector.
std::vector<std::shared_ptr<Rule>> rules; rules.push_back(std::make_shared<Rule1>()); #ifdef _WIN32 // Windows specific rules rules.push_back(std::make_shared<Rule2>()); rules.push_back(std::make_shared<Rule3>()); #elif __linux__ // Linux specific rules rules.push_back(std::make_shared<Rule4>()); #endif
Here's a code:
#include <memory> #include <typeindex> #include <unordered_map> template <typename Derived, typename Base> struct Registrar { template<typename ...Args> Registrar(Args&&... args) { Base::registry()[typeid(Derived)] = std::make_shared<Derived>(std::forward<Args>(args)...); } }; template<typename T> struct Registrable { typedef std::unordered_map<std::type_index, std::shared_ptr<T>> collection_t; template<typename R> using Registrar = Registrar<R, T>; static collection_t & registry() { static collection_t collection; return collection; } };I use std::unordered_map, because I want to make sure that I have only one instance of each class in my collection. If you accept, or even want to have multiple instances of the same class in the collection, you can replace it with std::vector, or any other collection.
And that's how the code above can be used:
class Base : public Registrable<Base> { public: virtual void print_myself() = 0; }; class WithParameter : public Base { int x; public: WithParameter(int x) : x(x) {} void print_myself() override { std::cout << "WithParameter(" << x << ")" << std::endl; } static Registrar<WithParameter> registrar; }; WithParameter::Registrar<WithParameter> WithParameter::registrar(25); class NoParameter : public Base { public: void print_myself() override { std::cout << "NoParameter" << std::endl; } static Registrar<NoParameter> registrar; }; NoParameter::Registrar<NoParameter> NoParameter::registrar;
I've created Base class, and two inherited classes (WithParameter and NoParameter). Both derived classes have static registrar object, which automatically adds instances of the classes to the collection.
Unfortunately, constructor parameters (if any), have to be compile time constants. But in my case, it's not a problem.
Now, we can e.g. enumerate our collection:
for (auto x : Base::registry()) { x.second->print_myself(); }
Full code with example can be found on my github's gist [1].
Links
[1] https://gist.github.com/loganek/c0c8dfb30cd72a5a1a47b3701b61d3da