diff options
-rw-r--r-- | include/gras/block.hpp | 36 | ||||
-rw-r--r-- | include/gras/detail/block.hpp | 18 | ||||
-rw-r--r-- | include/gras/detail/property.hpp | 8 | ||||
-rw-r--r-- | lib/block_props.cpp | 28 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 32 | ||||
-rw-r--r-- | tests/block_props_test.cpp | 66 |
6 files changed, 160 insertions, 28 deletions
diff --git a/include/gras/block.hpp b/include/gras/block.hpp index d3e9848..477ad8d 100644 --- a/include/gras/block.hpp +++ b/include/gras/block.hpp @@ -106,8 +106,9 @@ struct GRAS_API OutputPortConfig size_t maximum_items; }; -struct GRAS_API Block : Element +class GRAS_API Block : public Element { +public: //! Contruct an empty/null block Block(void); @@ -254,30 +255,43 @@ struct GRAS_API Block : Element * that is of the exact type associated with this property. * Otherwise, set_property with throw a type error. * - * Example with template argument to be type explicit: - * my_block->set_property<size_t>("foo", 42); + * Examples with explicit argument types: + * my_block->set<size_t>("foo", 42); + * my_block->set("foo", size_t(42)); * * \param key the string to identify this property * \param value the new value to set to this property */ template <typename ValueType> - void set_property(const std::string &key, const ValueType &value); + void set(const std::string &key, const ValueType &value); /*! - * Get the value of a registered property. + * Get the value of a registered property with reference semantics. * - * Note: the user must specify the correct value type + * Note: the user must be careful to only use a value * that is of the exact type associated with this property. * Otherwise, get_property with throw a type error. * - * Example with template argument to be type explicit: - * const size_t foo = my_block->get_property<size_t>("foo"); + * Example getting property with reference semantics: + * size_t foo; my_block->get("foo", foo); + * + * \param key the string to identify this property + * \param value a reference to set to the result + */ + template <typename ValueType> + void get(const std::string &key, ValueType &value); + + /*! + * Get the value of a registered property with return semantics. + * + * Example getting property with return value semantics: + * const size_t foo = my_block->get<size_t>("foo"); * * \param key the string to identify this property * \return the value of this property */ template <typename ValueType> - ValueType get_property(const std::string &key); + ValueType get(const std::string &key); /******************************************************************* * Work related routines and fail states @@ -429,9 +443,6 @@ struct GRAS_API Block : Element * This method is called by the scheduler to allocate output buffers. * The user may overload this method to create a custom allocator. * - * Example use case: - * //TODO code example - * * \param which_output the output port index number * \param config holds token and recommended length * \return a shared ptr to a new buffer queue object @@ -461,6 +472,7 @@ struct GRAS_API Block : Element /******************************************************************* * private implementation guts for template support ******************************************************************/ +private: void _register_property(const std::string &, PropertyRegistrySptr); void _set_property(const std::string &, const PMCC &); PMCC _get_property(const std::string &); diff --git a/include/gras/detail/block.hpp b/include/gras/detail/block.hpp index a8bc2b1..c464446 100644 --- a/include/gras/detail/block.hpp +++ b/include/gras/detail/block.hpp @@ -6,6 +6,10 @@ namespace gras { +/*! + * The following functions implement the templated methods in Block + */ + template <typename ClassType, typename ValueType> GRAS_FORCE_INLINE void Block::register_property( const std::string &key, @@ -14,18 +18,24 @@ GRAS_FORCE_INLINE void Block::register_property( ) { PropertyRegistrySptr pr; - pr.reset(new PropertyRegistryImpl<ClassType, ValueType>(this, get, set)); + pr.reset(new PropertyRegistryImpl<ClassType, ValueType>((ClassType *)this, get, set)); this->_register_property(key, pr); } template <typename ValueType> -GRAS_FORCE_INLINE void Block::set_property(const std::string &key, const ValueType &value) +GRAS_FORCE_INLINE void Block::set(const std::string &key, const ValueType &value) +{ + this->_set_property(key, PMC_M(value)); +} + +template <typename ValueType> +GRAS_FORCE_INLINE void Block::get(const std::string &key, ValueType &value) { - return this->_set_property(key, PMC_M(value)); + value = this->_get_property(key).as<ValueType>(); } template <typename ValueType> -GRAS_FORCE_INLINE ValueType Block::get_property(const std::string &key) +GRAS_FORCE_INLINE ValueType Block::get(const std::string &key) { return this->_get_property(key).as<ValueType>(); } diff --git a/include/gras/detail/property.hpp b/include/gras/detail/property.hpp index fd13025..2fcdca0 100644 --- a/include/gras/detail/property.hpp +++ b/include/gras/detail/property.hpp @@ -21,8 +21,9 @@ struct GRAS_API PropertyRegistry typedef boost::shared_ptr<PropertyRegistry> PropertyRegistrySptr; template <typename ClassType, typename ValueType> -struct PropertyRegistryImpl : PropertyRegistry +class PropertyRegistryImpl : public PropertyRegistry { +public: PropertyRegistryImpl( ClassType *my_class, ValueType(ClassType::*getter)(void), @@ -36,14 +37,15 @@ struct PropertyRegistryImpl : PropertyRegistry void set(const PMCC &value) { - return _setter(_my_class, value.as<ValueType>()); + (_my_class->*_setter)(value.as<ValueType>()); } PMCC get(void) { - return PMC_M(_getter(_my_class)); + return PMC_M((_my_class->*_getter)()); } +private: ClassType *_my_class; ValueType(ClassType::*_getter)(void); void(ClassType::*_setter)(const ValueType &); diff --git a/lib/block_props.cpp b/lib/block_props.cpp index bba9441..99edce7 100644 --- a/lib/block_props.cpp +++ b/lib/block_props.cpp @@ -8,18 +8,20 @@ using namespace gras; PropertyRegistry::PropertyRegistry(void){} PropertyRegistry::~PropertyRegistry(void){} +/*********************************************************************** + * The actual thread-safe implementation of property handling + **********************************************************************/ void BlockActor::handle_prop_access( const PropAccessMessage &message, const Theron::Address from ) { - ASSERT(this->prio_count.Load() != 0); - this->prio_count.Decrement(); - + //setup reply PropAccessMessage reply; reply.set = not message.set; reply.key = message.key; + //try to call the property bound method PropertyRegistrySptr pr = prop_registry[message.key]; if (not pr) reply.error = "no property registered for key: " + message.key; else try @@ -36,14 +38,14 @@ void BlockActor::handle_prop_access( reply.error = "unknown error"; } + //send the reply this->Send(reply, from); //ACK + this->highPrioAck(); } -void Block::_register_property(const std::string &key, PropertyRegistrySptr pr) -{ - (*this)->block->prop_registry[key] = pr; -} - +/*********************************************************************** + * A special receiver to handle the property access result + **********************************************************************/ struct PropAccessReceiver : Theron::Receiver { PropAccessReceiver(void) @@ -59,6 +61,9 @@ struct PropAccessReceiver : Theron::Receiver PropAccessMessage message; }; +/*********************************************************************** + * Handle the get and set calls from the user's call-stack + **********************************************************************/ PMCC BlockActor::prop_access_dispatcher(const std::string &key, const PMCC &value, const bool set) { PropAccessReceiver receiver; @@ -67,7 +72,7 @@ PMCC BlockActor::prop_access_dispatcher(const std::string &key, const PMCC &valu message.key = key; message.value = value; this->Push(message, receiver.GetAddress()); - this->prio_count.Increment(); + this->highPrioPreNotify(); receiver.Wait(); if (not receiver.message.error.empty()) { @@ -76,6 +81,11 @@ PMCC BlockActor::prop_access_dispatcher(const std::string &key, const PMCC &valu return receiver.message.value; } +void Block::_register_property(const std::string &key, PropertyRegistrySptr pr) +{ + (*this)->block->prop_registry[key] = pr; +} + void Block::_set_property(const std::string &key, const PMCC &value) { (*this)->block->prop_access_dispatcher(key, value, true); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 27a2965..23a834e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -4,6 +4,38 @@ include(GrTest) +######################################################################## +# unit test suite +######################################################################## +find_package(Boost COMPONENTS unit_test_framework) + +if (NOT Boost_FOUND) + return() +endif() + +set(test_sources + block_props_test.cpp +) + +include_directories(${GRAS_INCLUDE_DIRS}) +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +#turn each test cpp file into an executable with an int main() function +add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) + +#for each source: build an executable, register it as a test +foreach(test_source ${test_sources}) + get_filename_component(test_name ${test_source} NAME_WE) + add_executable(${test_name} ${test_source}) + target_link_libraries(${test_name} ${Boost_LIBRARIES} ${GRAS_LIBRARIES}) + set(GR_TEST_LIBRARY_DIRS ${Boost_LIBRARY_DIRS}) + GR_ADD_TEST(${test_name} ${test_name}) +endforeach(test_source) + +######################################################################## +# Python unit tests +######################################################################## include(GrPython) set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B}) diff --git a/tests/block_props_test.cpp b/tests/block_props_test.cpp new file mode 100644 index 0000000..ded4273 --- /dev/null +++ b/tests/block_props_test.cpp @@ -0,0 +1,66 @@ +// 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(void): + gras::Block("MyBlock") + { + foo = 0; + this->register_property("foo", &MyBlock::get_foo, &MyBlock::set_foo); + } + + //dummy work + void work(const InputItems &, const OutputItems &){} + + size_t get_foo(void) + { + return foo; + } + + void set_foo(const size_t &new_foo) + { + foo = new_foo; + } + + size_t foo; +}; + +BOOST_AUTO_TEST_CASE(test_property_set_get_with_return) +{ + MyBlock my_block; + BOOST_CHECK_EQUAL(my_block.foo, size_t(0)); + + my_block.set("foo", size_t(42)); + BOOST_CHECK_EQUAL(my_block.foo, size_t(42)); + + const size_t my_foo = my_block.get<size_t>("foo"); + BOOST_CHECK_EQUAL(my_foo, size_t(42)); +} + +BOOST_AUTO_TEST_CASE(test_property_set_get_with_reference) +{ + MyBlock my_block; + BOOST_CHECK_EQUAL(my_block.foo, size_t(0)); + + my_block.set("foo", size_t(42)); + BOOST_CHECK_EQUAL(my_block.foo, size_t(42)); + + size_t my_foo; my_block.get("foo", my_foo); + BOOST_CHECK_EQUAL(my_foo, size_t(42)); +} + +BOOST_AUTO_TEST_CASE(test_property_errors) +{ + MyBlock my_block; + + //property does not exist + BOOST_CHECK_THROW(my_block.get<size_t>("bar"), std::exception); + + //wrong type for property + BOOST_CHECK_THROW(my_block.set("foo", double(42)), std::exception); +} |