Adding GlobalFactory class for making registration functions.
Change-Id: I2374c84211a321eba739193f7f7554be43b47cdc
diff --git a/aos/common/util/BUILD b/aos/common/util/BUILD
index b62e93c..4e0455b 100644
--- a/aos/common/util/BUILD
+++ b/aos/common/util/BUILD
@@ -188,6 +188,24 @@
)
cc_library(
+ name = 'global_factory',
+ hdrs = [
+ 'global_factory.h'
+ ],
+)
+
+cc_test(
+ name = 'global_factory_test',
+ srcs = [
+ 'global_factory_test.cc'
+ ],
+ deps = [
+ '//aos/testing:googletest',
+ ':global_factory'
+ ],
+)
+
+cc_library(
name = 'linked_list',
hdrs = [
'linked_list.h',
diff --git a/aos/common/util/global_factory.h b/aos/common/util/global_factory.h
new file mode 100644
index 0000000..148fc1e
--- /dev/null
+++ b/aos/common/util/global_factory.h
@@ -0,0 +1,88 @@
+#ifndef AOS_COMMON_UTIL_GLOBAL_FACTORY_H_
+#define AOS_COMMON_UTIL_GLOBAL_FACTORY_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+// // File Usage Description:
+// class ExampleBaseClass { virtual ~ExampleBaseClass(); }
+// class ExampleSubClass : public ExampleBaseClass {}
+//
+// // At namespace scope in header file:
+// SETUP_FACTORY(ExampleBaseClass);
+// // At namespace scope in cc file:
+// REGISTER_SUBCLASS("ExampleSubClass", ExampleBaseClass, ExampleSubClass);
+//
+// // When you want an object of type "ExampleSubClass".
+// std::unique_ptr<ExampleBaseClass> constructed_item =
+// ExampleBaseClassGlobalFactory::Get("ExampleSubClass")();
+
+// Helper macro to set up a Factory Family for a particular type.
+// Put this is the header file along-side the base class.
+#define SETUP_FACTORY(BaseClass, ...) \
+ using BaseClass##GlobalFactory = \
+ ::aos::GlobalFactory<BaseClass, ##__VA_ARGS__>
+
+// Helper macro to set up a Factory for a subtype. For BaseClass
+// This should happen in a .cc file not a header file to avoid multiple
+// linkage.
+#define REGISTER_SUBCLASS_BY_KEY(key, BaseClass, SubClass) \
+ BaseClass##GlobalFactory::SubClassRegisterer<SubClass> \
+ register_for_##SubClass(key)
+
+// Proxy to above but where SubClass name is the key.
+#define REGISTER_SUBCLASS(BaseClass, SubClass) \
+ REGISTER_SUBCLASS_BY_KEY(#SubClass, BaseClass, SubClass)
+
+namespace aos {
+
+// Maintains a static std::unordered_map<std::string, FactoryFunction> for
+// BaseClass. Best to use with macros above.
+template <typename BaseClass, typename... FactoryArgs>
+class GlobalFactory {
+ public:
+ using FactoryFunction =
+ std::function<std::unique_ptr<BaseClass>(FactoryArgs&&...)>;
+
+ // Gets the factory function by named. This will return a null factory
+ // std::function if the factory is not available, so one would be wise
+ // to check this function before use.
+ // It is an error to call this during static initialization.
+ static const FactoryFunction &Get(const std::string &name) {
+ const auto &map = *GetMap();
+ auto item = map.find(name);
+ if (item == map.end()) {
+ static FactoryFunction null_create_fn;
+ return null_create_fn;
+ }
+ return item->second;
+ }
+
+ // Installs a factory function for constructing SubClass
+ // using name "name". It is an error not call this at namespace scope
+ // through the REGISTER_SUBCLASS macro above.
+ template <typename SubClass>
+ class SubClassRegisterer {
+ public:
+ explicit SubClassRegisterer(const char *name) {
+ (*GetMap())[name] = [](FactoryArgs&&... args) {
+ return std::unique_ptr<BaseClass>(
+ new SubClass(std::forward<FactoryArgs>(args)...));
+ };
+ }
+ };
+
+ private:
+ // Actual map. (Protected by static from concurrent construction
+ // if there is nothing registered at static linkage time).
+ static std::unordered_map<std::string, FactoryFunction> *GetMap() {
+ static std::unordered_map<std::string, FactoryFunction> map;
+ return ↦
+ }
+};
+
+} // namespace aos
+
+#endif // AOS_COMMON_UTIL_GLOBAL_FACTORY_H_
diff --git a/aos/common/util/global_factory_test.cc b/aos/common/util/global_factory_test.cc
new file mode 100644
index 0000000..8da7dcb
--- /dev/null
+++ b/aos/common/util/global_factory_test.cc
@@ -0,0 +1,67 @@
+#include "aos/common/util/global_factory.h"
+#include "gtest/gtest.h"
+
+namespace aos {
+
+namespace test_a {
+class BaseType {
+ public:
+ virtual ~BaseType() {}
+
+ virtual std::pair<int, int> Get() = 0;
+};
+
+SETUP_FACTORY(BaseType, int, int);
+
+class BaseTypeNoArgs {
+ public:
+ virtual ~BaseTypeNoArgs() {}
+
+ virtual int Get() = 0;
+};
+
+SETUP_FACTORY(BaseTypeNoArgs);
+
+} // namespace test_a
+
+namespace test_b {
+
+class SubType : public test_a::BaseType {
+ public:
+ SubType(int t1, int t2) : value_(t1, t2) {}
+ std::pair<int, int> Get() override { return value_; }
+
+ private:
+ std::pair<int, int> value_;
+};
+
+REGISTER_SUBCLASS(test_a::BaseType, SubType);
+
+} // namespace test_b
+
+namespace {
+
+class SubType1 : public test_a::BaseTypeNoArgs {
+ public:
+ int Get() override { return 1; }
+};
+
+class SubType2 : public test_a::BaseTypeNoArgs {
+ public:
+ int Get() override { return 2; }
+};
+REGISTER_SUBCLASS(test_a::BaseTypeNoArgs, SubType1);
+REGISTER_SUBCLASS(test_a::BaseTypeNoArgs, SubType2);
+
+TEST(GlobalFactoryTest, CheckFactory) {
+ auto val = test_a::BaseTypeGlobalFactory::Get("SubType")(2, 7)->Get();
+ EXPECT_EQ(val.first, 2);
+ EXPECT_EQ(val.second, 7);
+}
+TEST(GlobalFactoryTest, CheckFactoryNoArgs) {
+ EXPECT_EQ(1, test_a::BaseTypeNoArgsGlobalFactory::Get("SubType1")()->Get());
+ EXPECT_EQ(2, test_a::BaseTypeNoArgsGlobalFactory::Get("SubType2")()->Get());
+}
+
+} // namespace
+} // namespace aos