diff options
author | Josh Blum | 2013-07-07 11:50:03 -0700 |
---|---|---|
committer | Josh Blum | 2013-07-07 11:50:03 -0700 |
commit | c6373f53f4690d667e553ed7258e13c3b45aa323 (patch) | |
tree | 202bf0d31140c5567cad54dea697ad1dcbba6432 | |
parent | e2a237e121edfb53378ae57d8faa71491d0f29f1 (diff) | |
download | sandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.tar.gz sandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.tar.bz2 sandhi-c6373f53f4690d667e553ed7258e13c3b45aa323.zip |
gras: created element factory
-rw-r--r-- | include/gras/CMakeLists.txt | 2 | ||||
-rw-r--r-- | include/gras/detail/factory.hpp | 151 | ||||
-rw-r--r-- | include/gras/factory.hpp | 85 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/callable.cpp | 1 | ||||
-rw-r--r-- | lib/factory.cpp | 49 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/factory_test.cpp | 77 |
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); +} |