summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Blum2013-07-07 11:50:03 -0700
committerJosh Blum2013-07-07 11:50:03 -0700
commitc6373f53f4690d667e553ed7258e13c3b45aa323 (patch)
tree202bf0d31140c5567cad54dea697ad1dcbba6432
parente2a237e121edfb53378ae57d8faa71491d0f29f1 (diff)
downloadsandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.tar.gz
sandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.tar.bz2
sandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.zip
gras: created element factory
-rw-r--r--include/gras/CMakeLists.txt2
-rw-r--r--include/gras/detail/factory.hpp151
-rw-r--r--include/gras/factory.hpp85
-rw-r--r--lib/CMakeLists.txt1
-rw-r--r--lib/callable.cpp1
-rw-r--r--lib/factory.cpp49
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/factory_test.cpp77
8 files changed, 367 insertions, 0 deletions
diff --git a/include/gras/CMakeLists.txt b/include/gras/CMakeLists.txt
index 38cb233..72d2fa6 100644
--- a/include/gras/CMakeLists.txt
+++ b/include/gras/CMakeLists.txt
@@ -11,6 +11,7 @@ install(FILES
block.i
element.hpp
element.i
+ factory.hpp
gras.hpp
hier_block.hpp
hier_block.i
@@ -39,6 +40,7 @@ install(FILES
detail/block.hpp
detail/chrono.hpp
detail/element.hpp
+ detail/factory.hpp
detail/sbuffer.hpp
detail/work_buffer.hpp
diff --git a/include/gras/detail/factory.hpp b/include/gras/detail/factory.hpp
new file mode 100644
index 0000000..0487e76
--- /dev/null
+++ b/include/gras/detail/factory.hpp
@@ -0,0 +1,151 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#ifndef INCLUDED_GRAS_DETAIL_FACTORY_HPP
+#define INCLUDED_GRAS_DETAIL_FACTORY_HPP
+
+#include <PMC/Containers.hpp> //PMCList
+
+namespace gras
+{
+
+/***********************************************************************
+ * Factory entry base class
+ **********************************************************************/
+struct GRAS_API FactoryRegistryEntry
+{
+ FactoryRegistryEntry(void);
+ virtual ~FactoryRegistryEntry(void);
+ virtual Element *make(const PMCC &args) = 0;
+};
+
+/***********************************************************************
+ * Templated registration - 0 args
+ **********************************************************************/
+template <typename ReturnType>
+struct FactoryRegistryEntryImpl0 : FactoryRegistryEntry
+{
+ typedef ReturnType(*Fcn)(void);
+ FactoryRegistryEntryImpl0(Fcn fcn):_fcn(fcn){}
+ Element *make(const PMCC &)
+ {
+ return _fcn();
+ }
+ Fcn _fcn;
+};
+
+template <typename ReturnType>
+void Factory::register_make(const std::string &name, ReturnType(*fcn)(void))
+{
+ void *r = new FactoryRegistryEntryImpl0<ReturnType>(fcn);
+ Factory::_register_make(name, r);
+}
+
+/***********************************************************************
+ * Templated registration - 1 args
+ **********************************************************************/
+template <typename ReturnType, typename Arg0>
+struct FactoryRegistryEntryImpl1 : FactoryRegistryEntry
+{
+ typedef ReturnType(*Fcn)(const Arg0 &);
+ FactoryRegistryEntryImpl1(Fcn fcn):_fcn(fcn){}
+ Element *make(const PMCC &args)
+ {
+ const PMCList &a = args.as<PMCList>();
+ return _fcn(a[0].safe_as<Arg0>());
+ }
+ Fcn _fcn;
+};
+
+template <typename ReturnType, typename Arg0>
+void Factory::register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &))
+{
+ void *r = new FactoryRegistryEntryImpl1<ReturnType, Arg0>(fcn);
+ Factory::_register_make(name, r);
+}
+
+/***********************************************************************
+ * Templated registration - 2 args
+ **********************************************************************/
+template <typename ReturnType, typename Arg0, typename Arg1>
+struct FactoryRegistryEntryImpl2 : FactoryRegistryEntry
+{
+ typedef ReturnType(*Fcn)(const Arg0 &, const Arg1 &);
+ FactoryRegistryEntryImpl2(Fcn fcn):_fcn(fcn){}
+ Element *make(const PMCC &args)
+ {
+ const PMCList &a = args.as<PMCList>();
+ return _fcn(a[0].safe_as<Arg0>(), a[1].safe_as<Arg1>());
+ }
+ Fcn _fcn;
+};
+
+template <typename ReturnType, typename Arg0, typename Arg1>
+void Factory::register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &, const Arg1 &))
+{
+ void *r = new FactoryRegistryEntryImpl2<ReturnType, Arg0, Arg1>(fcn);
+ Factory::_register_make(name, r);
+}
+
+/***********************************************************************
+ * Templated registration - 3 args
+ **********************************************************************/
+template <typename ReturnType, typename Arg0, typename Arg1, typename Arg2>
+struct FactoryRegistryEntryImpl3 : FactoryRegistryEntry
+{
+ typedef ReturnType(*Fcn)(const Arg0 &, const Arg1 &, const Arg2 &);
+ FactoryRegistryEntryImpl3(Fcn fcn):_fcn(fcn){}
+ Element *make(const PMCC &args)
+ {
+ const PMCList &a = args.as<PMCList>();
+ return _fcn(a[0].safe_as<Arg0>(), a[1].safe_as<Arg1>(), a[2].safe_as<Arg2>());
+ }
+ Fcn _fcn;
+};
+
+template <typename ReturnType, typename Arg0, typename Arg1, typename Arg2>
+void Factory::register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &, const Arg1 &, const Arg2 &))
+{
+ void *r = new FactoryRegistryEntryImpl3<ReturnType, Arg0, Arg1, Arg2>(fcn);
+ Factory::_register_make(name, r);
+}
+
+/***********************************************************************
+ * Templated make implementations
+ **********************************************************************/
+inline
+Element *Factory::make(const std::string &name)
+{
+ PMCList args(0);
+ return Factory::_make(name, PMC_M(args));
+}
+
+template <typename Arg0>
+Element *Factory::make(const std::string &name, const Arg0 &a0)
+{
+ PMCList args(1);
+ args[0] = PMC_M(a0);
+ return Factory::_make(name, PMC_M(args));
+}
+
+template <typename Arg0, typename Arg1>
+Element *Factory::make(const std::string &name, const Arg0 &a0, const Arg1 &a1)
+{
+ PMCList args(2);
+ args[0] = PMC_M(a0);
+ args[1] = PMC_M(a1);
+ return Factory::_make(name, PMC_M(args));
+}
+
+template <typename Arg0, typename Arg1, typename Arg2>
+Element *Factory::make(const std::string &name, const Arg0 &a0, const Arg1 &a1, const Arg2 &a2)
+{
+ PMCList args(3);
+ args[0] = PMC_M(a0);
+ args[1] = PMC_M(a1);
+ args[2] = PMC_M(a2);
+ return Factory::_make(name, PMC_M(args));
+}
+
+}
+
+#endif /*INCLUDED_GRAS_DETAIL_FACTORY_HPP*/
diff --git a/include/gras/factory.hpp b/include/gras/factory.hpp
new file mode 100644
index 0000000..926025e
--- /dev/null
+++ b/include/gras/factory.hpp
@@ -0,0 +1,85 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#ifndef INCLUDED_GRAS_FACTORY_HPP
+#define INCLUDED_GRAS_FACTORY_HPP
+
+#include <gras/gras.hpp>
+#include <gras/element.hpp>
+#include <PMC/PMC.hpp>
+#include <string>
+
+//! A fixture for static initialization code
+#define GRAS_STATIC_BLOCK(name) \
+ static struct name ## _static_fixture__ \
+ { \
+ name ## _static_fixture__(void); \
+ } name ## _static_fixture_instance__; \
+ name ## _static_fixture__::name ## _static_fixture__(void)
+
+/*!
+ * Register a block's factory function:
+ * Declare this macro at the global scope in a cpp file.
+ * The block will register at static initialization time.
+ */
+#define GRAS_REGISTER_FACTORY(name) \
+ GRAS_STATIC_BLOCK(name) \
+ {gras::Factory::register_make(#name, &name);}
+
+namespace gras
+{
+
+/*!
+ * Element factory:
+ * - Register factory functions into the global factory.
+ * - Call make() to create element from global factory.
+ *
+ * Example register a factory function:
+ * gras::Factory::register_make("make_my_block", &make_my_block);
+ *
+ * Example call into the factory:
+ * gras::Element *my_block = gras::Factory::make("make_my_block", arg0, arg1);
+ */
+class GRAS_API Factory : Callable
+{
+public:
+
+ /*******************************************************************
+ * Register API - don't look here, template magic, not helpful
+ ******************************************************************/
+ template <typename ReturnType>
+ static void register_make(const std::string &name, ReturnType(*fcn)(void));
+
+ template <typename ReturnType, typename Arg0>
+ static void register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &));
+
+ template <typename ReturnType, typename Arg0, typename Arg1>
+ static void register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &, const Arg1 &));
+
+ template <typename ReturnType, typename Arg0, typename Arg1, typename Arg2>
+ static void register_make(const std::string &name, ReturnType(*fcn)(const Arg0 &, const Arg1 &, const Arg2 &));
+
+ /*******************************************************************
+ * Make API - don't look here, template magic, not helpful
+ ******************************************************************/
+ inline
+ static Element *make(const std::string &name);
+
+ template <typename Arg0>
+ static Element *make(const std::string &name, const Arg0 &a0);
+
+ template <typename Arg0, typename Arg1>
+ static Element *make(const std::string &name, const Arg0 &a0, const Arg1 &a1);
+
+ template <typename Arg0, typename Arg1, typename Arg2>
+ static Element *make(const std::string &name, const Arg0 &a0, const Arg1 &a1, const Arg2 &a2);
+
+private:
+ static void _register_make(const std::string &, void *);
+ static Element *_make(const std::string &, const PMCC &);
+};
+
+}
+
+#include <gras/detail/factory.hpp>
+
+#endif /*INCLUDED_GRAS_FACTORY_HPP*/
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 38216b3..93ef63c 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -44,6 +44,7 @@ list(APPEND GRAS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/callable.cpp
${CMAKE_CURRENT_SOURCE_DIR}/element.cpp
${CMAKE_CURRENT_SOURCE_DIR}/element_uid.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/factory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sbuffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/circular_buffer.cpp
${CMAKE_CURRENT_SOURCE_DIR}/buffer_queue_circ.cpp
diff --git a/lib/callable.cpp b/lib/callable.cpp
index af27399..a7bc7da 100644
--- a/lib/callable.cpp
+++ b/lib/callable.cpp
@@ -42,6 +42,7 @@ void Callable::unregister_call(const std::string &name)
void Callable::_register_call(const std::string &name, void *entry)
{
CallableRegistry *cr = reinterpret_cast<CallableRegistry *>(_call_registry.get());
+ if (cr->count(name) != 0) throw std::invalid_argument("Callable - method already registered for name: " + name);
(*cr)[name].reset(reinterpret_cast<CallableRegistryEntry *>(entry));
}
diff --git a/lib/factory.cpp b/lib/factory.cpp
new file mode 100644
index 0000000..1c634fd
--- /dev/null
+++ b/lib/factory.cpp
@@ -0,0 +1,49 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#include <gras/factory.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include <stdexcept>
+#include <map>
+
+using namespace gras;
+
+FactoryRegistryEntry::FactoryRegistryEntry(void)
+{
+ //NOP
+}
+
+FactoryRegistryEntry::~FactoryRegistryEntry(void)
+{
+ //NOP
+}
+
+typedef std::map<std::string, boost::shared_ptr<FactoryRegistryEntry> > FactoryRegistryType;
+
+static FactoryRegistryType &get_factory_registry(void)
+{
+ static FactoryRegistryType r;
+ return r;
+}
+
+static boost::mutex mutex;
+
+void Factory::_register_make(const std::string &name, void *entry)
+{
+ boost::mutex::scoped_lock l(mutex);
+ if (get_factory_registry().count(name) != 0)
+ {
+ throw std::invalid_argument("Factory - function already registered for name: " + name);
+ }
+ get_factory_registry()[name].reset(reinterpret_cast<FactoryRegistryEntry *>(entry));
+}
+
+Element *Factory::_make(const std::string &name, const PMCC &args)
+{
+ boost::mutex::scoped_lock l(mutex);
+ if (get_factory_registry().count(name) == 0)
+ {
+ throw std::invalid_argument("Factory - no function registered for name: " + name);
+ }
+ return get_factory_registry()[name]->make(args);
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index bb3f3c8..62cf7d8 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,6 +17,7 @@ set(test_sources
callable_test.cpp
chrono_time_test.cpp
block_props_test.cpp
+ factory_test.cpp
serialize_tags_test.cpp
)
diff --git a/tests/factory_test.cpp b/tests/factory_test.cpp
new file mode 100644
index 0000000..00239f9
--- /dev/null
+++ b/tests/factory_test.cpp
@@ -0,0 +1,77 @@
+// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
+
+#include <boost/test/unit_test.hpp>
+#include <iostream>
+
+#include <gras/block.hpp>
+
+struct MyBlock : gras::Block
+{
+ MyBlock(const long &a0 = 0, const std::string &a1 = "", const bool &a2 = false):
+ gras::Block("MyBlock"), a0(a0), a1(a1), a2(a2)
+ {
+ }
+
+ const long a0;
+ const std::string a1;
+ const bool a2;
+
+ //dummy work
+ void work(const InputItems &, const OutputItems &){}
+};
+
+// a few test factory functions
+
+gras::Block *make_my_block_args0(void)
+{
+ return new MyBlock();
+}
+
+gras::Block *make_my_block_args1(const long &a0)
+{
+ return new MyBlock(a0);
+}
+
+gras::Block *make_my_block_args2(const long &a0, const std::string &a1)
+{
+ return new MyBlock(a0, a1);
+}
+
+gras::Block *make_my_block_args3(const long &a0, const std::string &a1, const bool &a2)
+{
+ return new MyBlock(a0, a1, a2);
+}
+
+#include <gras/factory.hpp>
+
+GRAS_REGISTER_FACTORY(make_my_block_args0)
+GRAS_REGISTER_FACTORY(make_my_block_args1)
+GRAS_REGISTER_FACTORY(make_my_block_args2)
+GRAS_REGISTER_FACTORY(make_my_block_args3)
+
+BOOST_AUTO_TEST_CASE(test_register_and_make)
+{
+ gras::Element *my_block0 = gras::Factory::make("make_my_block_args0");
+ BOOST_CHECK(dynamic_cast<MyBlock *>(my_block0) != NULL);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block0)->a0, 0);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block0)->a1, "");
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block0)->a2, false);
+
+ gras::Element *my_block1 = gras::Factory::make("make_my_block_args1", 42);
+ BOOST_CHECK(dynamic_cast<MyBlock *>(my_block1) != NULL);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block1)->a0, 42);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block1)->a1, "");
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block1)->a2, false);
+
+ gras::Element *my_block2 = gras::Factory::make("make_my_block_args2", 1, "foo");
+ BOOST_CHECK(dynamic_cast<MyBlock *>(my_block2) != NULL);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block2)->a0, 1);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block2)->a1, "foo");
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block2)->a2, false);
+
+ gras::Element *my_block3 = gras::Factory::make("make_my_block_args3", -2, "bar", true);
+ BOOST_CHECK(dynamic_cast<MyBlock *>(my_block3) != NULL);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block3)->a0, -2);
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block3)->a1, "bar");
+ BOOST_CHECK_EQUAL(dynamic_cast<MyBlock *>(my_block3)->a2, true);
+}