diff --git a/alib2common/src/core/components.hpp b/alib2common/src/core/components.hpp new file mode 100644 index 0000000000000000000000000000000000000000..13771894e78d1c791b5aa42733e37306216cf32d --- /dev/null +++ b/alib2common/src/core/components.hpp @@ -0,0 +1,400 @@ +/* + * Component.hpp + * + * Created on: Mar 16, 2016 + * Author: Jan Travnicek + */ + +#ifndef COMPONENT_HPP_ +#define COMPONENT_HPP_ + +#include <set> +#include <algorithm> +#include <utility> +#include <tuple> +#include "../exception/CommonException.h" + +namespace std { + +/** + * Represents a set of elements. + * @param Derived class representing datatype using this set. + * @param SymbolType underlying type of symbols in the set. + * @param SetType arbitrary type used to distinguish different sets. + */ +template < class Derived, class SymbolType, class SetType > +class Set { + /** + * The set. + */ + std::set < SymbolType > data; + + /** + * Checks whether symbol can be added to the set. Calls valid and available functions. + * @throws CommonException if symbol cannot be added. + */ + void checkAdd ( const SymbolType & symbol ) { + valid ( symbol ); + + if ( !available ( symbol ) ) + throw ::exception::CommonException ( "Symbol " + ( std::string ) symbol + " is not available." ); + } + + /** + * Checks whether symbol can be removed from the set. Calls used function. + * @throws CommonException if symbol cannot be removed. + */ + void checkRemove ( const SymbolType & symbol ) { + if ( used ( symbol ) ) + throw ::exception::CommonException ( "Symbol " + ( std::string ) symbol + " is used." ); + } + +protected: + /** Checks the state of the set. + */ + void checkState ( ) { + for ( const SymbolType & symbol : data ) + checkAdd ( symbol ); + } + +public: + /* + * Constructs an empty set. + */ + Set ( ) { + } + + /** + * Constructs a set containing given elements. + * @throw CommonException if elements are not available in context of datatype where the set is used + */ + Set ( std::set < SymbolType > symbols ) : data ( std::move ( symbols ) ) { + } + + /** + * Adds an elements to the set. + * @param element to add to the set + * @throw CommonException if element is not available in context of datatype where the set is used + * @return true if element was indeed added + * false if element was present in the set + */ + bool add ( SymbolType symbol ) { + checkAdd ( symbol ); + return data.insert ( std::move ( symbol ) ).second; + } + + /** + * Adds a set of elements to the set. + * @param elements to add to the set + * @throw CommonException if one of the elements is not available in context of datatype where the set is used + */ + void add ( std::set < SymbolType > symbols ) { + for ( SymbolType symbol : std::make_moveable_set ( symbols ) ) + add ( std::move ( symbol ) ); + } + + /** + * Changes the set. + * @param elements by which to replace those currently in the set + * @throw CommonException if one of the removed elements is used in context of datatype where the set is used + * CommonException if one of the added elements is not available in context of datatype where the set is used + */ + void set ( std::set < SymbolType > symbols ) { + std::set < SymbolType > removed; + std::set_difference ( data.begin ( ), data.end ( ), symbols.begin ( ), symbols.end ( ), std::inserter ( removed, removed.end ( ) ) ); + + for ( const SymbolType & symbol : removed ) + checkRemove ( symbol ); + + for ( const SymbolType & symbol : symbols ) + checkAdd ( symbol ); + + data = std::move ( symbols ); + } + + /** + * @return the set. + */ + std::set < SymbolType > & get ( ) { + return data; + } + + /** + * @return the set. + */ + const std::set < SymbolType > & get ( ) const { + return data; + } + + /** + * Removes an element from the set if not used. + * @throw CommonException if element is used in context of datatype where the set is used + * @return true if element was indeed removed + * false if element was not present in the set + */ + bool remove ( const SymbolType & symbol ) { + checkRemove ( symbol ); + return data.erase ( symbol ); + } + + /** + * Removes a set of elements from alphabet if not used. + * @throw CommonException if element is used in context of datatype where the set is used + */ + void remove ( const std::set < SymbolType > & symbols ) { + for ( const SymbolType & symbol : symbols ) + remove ( symbol ); + } + + /** + * Set emptiness checker. + * @return true if set is an empty + */ + bool empty ( ) const { + return data.empty ( ); + } + + /** + * Checks whether a concrete element is used in context of the datatype where the set is used + * + * To be implemented by all template instantiations explicitly + * + * @param element to check + * @return true if element is used + * false if element is not used + */ + bool used ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete element is available in context of the datatype where the set is used + * + * To be implemented by all template instantiations explicitly + * + * @param element to check + * @return true if element is available + * false if element is not available + */ + bool available ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete element is valid in context of the datatype where the set is used + * + * To be implemented by all template instantiations explicitly + * + * @param element to check + * @throw CommonException if the element in any way invalid + */ + void valid ( const SymbolType & symbol ) const; +}; + +/** + * Represents an notable element. + * @param Derived class representing datatype using this notable element. + * @param SymbolType underlying type of element. + * @param ElementType arbitrary type used to distinguish different notable elements. + */ +template < class Derived, class SymbolType, class ElementType > +class Element { + /** + * The element. + */ + SymbolType data; + + /** + * Checks whether element can be set. Calls valid and available functions. + * @throws CommonException if element cannot be added. + */ + void checkSet ( const SymbolType & symbol ) { + valid ( symbol ); + + if ( !available ( symbol ) ) + throw::exception::CommonException ( "Symbol " + ( std::string ) symbol + " is not available." ); + } + +protected: + /** Checks the state of the element. + */ + void checkState ( ) { + checkSet ( data ); + } + +public: + /** + * Constructs a notable element. + * @throw CommonException if element is not available in context of datatype where the class is used + */ + Element ( SymbolType symbol ) : data ( std::move ( symbol ) ) { + } + + /** + * Changes the notable element. + * @param new value of the element + * @return bool true if the element was set + * false if the element was the same as already present + * @throw CommonException if the new element is not available in context of datatype where the class is used + */ + bool set ( SymbolType symbol ) { + checkSet ( symbol ); + + if ( data == symbol ) return false; + + data = std::move ( symbol ); + return true; + } + + /** + * Returns the current notable element of ElementType. + * @return the notable element + */ + SymbolType & get ( ) { + return data; + } + + /** + * Returns the current notable element of ElementType. + * @return the notable element + */ + const SymbolType & get ( ) const { + return data; + } + + /** + * Checks whether a concrete symbol is available in context of the datatype instance using this class + * + * To be implemented by all template instantiations explicitly + * + * @param element to check + * @return true if element is available + * false if element is not available + */ + bool available ( const SymbolType & symbol ) const; + + /** + * Checks whether a concrete symbol is valid in context of the datatype instance using this class + * + * To be implemented by all template instantiations explicitly + * + * @param element to check + * @throw CommonException if the element in any way invalid + */ + void valid ( const SymbolType & symbol ) const; +}; + +/** + * Auxiliary base handling all sets of elements from components + */ +template < class Derived, class SymbolType, class ... SetTypes > +struct SetAux; + +/** + * Specialisation for tuple. + */ +template < class Derived, class SymbolType, class ... SetTypes > +struct SetAux < Derived, SymbolType, tuple < SetTypes ... > > : public Set < Derived, SymbolType, SetTypes > ... { + + /** + * Constructor + */ + template < class ... RealSetTypes, size_t ... Indexes > + SetAux ( tuple < RealSetTypes ... > params, std::index_sequence < Indexes ... > ) : Set < Derived, SymbolType, SetTypes > ( std::move ( get < Indexes > ( params ) ) ) ... { + } + + /** + * Allows access to sub-component using its type. + * @param SetType alphabet type used to distinguish different sub alphabets + * @return sub-component + */ + template < class SetType > + const Set < Derived, SymbolType, SetType > & accessSet ( ) const { + return static_cast < const Set < Derived, SymbolType, SetType > & > ( * this ); + } + + /** + * Allows access to sub-component using its type. + * @param SetType alphabet type used to distinguish different sub alphabets + * @return sub-component + */ + template < class SetType > + Set < Derived, SymbolType, SetType > & accessSet ( ) { + return static_cast < Set < Derived, SymbolType, SetType > & > ( * this ); + } + +protected: + /** + * postponed checker function + */ + void checkState ( ) { + std::initializer_list < int > { ( Set < Derived, SymbolType, SetTypes >::checkState ( ), 0 ) ... }; + } + +}; + +/** + * Auxiliary base handling all notable elements from components + */ +template < class Derived, class SymbolType, class ... ElementTypes > +struct ElementAux; + +/** + * Specialisation for tuple. + */ +template < class Derived, class SymbolType, class ... ElementTypes > +struct ElementAux < Derived, SymbolType, tuple < ElementTypes ... > > : public Element < Derived, SymbolType, ElementTypes > ... { + + /** + * Constructor + */ + template < class ... RealElementTypes, size_t ... Indexes > + ElementAux ( tuple < RealElementTypes ... > params, std::index_sequence < Indexes ... > ) : Element < Derived, SymbolType, ElementTypes > ( std::move ( get < Indexes > ( params ) ) ) ... { + } + + /** + * Allows access to sub-component using its type. + * @param ElementType alphabet type used to distinguish different sub alphabets + * @return sub-component + */ + template < class ElementType > + const Element < Derived, SymbolType, ElementType > & accessElement ( ) const { + return static_cast < const Element < Derived, SymbolType, ElementType > & > ( * this ); + } + + /** + * Allows access to sub-component using its type. + * @param ElementType alphabet type used to distinguish different sub alphabets + * @return sub-component + */ + template < class ElementType > + Element < Derived, SymbolType, ElementType > & accessElement ( ) { + return static_cast < Element < Derived, SymbolType, ElementType > & > ( * this ); + } + +protected: + /** + * postponed checker function + */ + void checkState ( ) { + std::initializer_list < int > { ( Element < Derived, SymbolType, ElementTypes >::checkState ( ), 0 ) ... }; + } + +}; + +/** + * Auxiliary class allowing simple access to the alphabets. + */ +template < class Derived, class SymbolType, class SetTypesPack, class ElementTypesPack > +class Components : public SetAux < Derived, SymbolType, SetTypesPack >, public ElementAux < Derived, SymbolType, ElementTypesPack > { +public: + /** + * Construct an alphabet pack from two alphabets. + */ + template < class RealSetTypes, class RealElementTypes > + Components ( RealSetTypes params1, RealElementTypes params2 ) : SetAux < Derived, SymbolType, SetTypesPack > ( std::move ( params1 ), std::make_index_sequence < std::tuple_size < typename std::decay < SetTypesPack >::type >::value > { } ), ElementAux < Derived, SymbolType, ElementTypesPack > ( std::move ( params2 ), std::make_index_sequence < std::tuple_size < typename std::decay < ElementTypesPack >::type >::value > { } ) { + SetAux < Derived, SymbolType, SetTypesPack >::checkState ( ); + + ElementAux < Derived, SymbolType, ElementTypesPack >::checkState ( ); + } +}; + +} /* namespace std */ + +#endif /* COMPONENT_HPP_ */ diff --git a/alib2common/test-src/core/ComponentsTest.cpp b/alib2common/test-src/core/ComponentsTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..63e60e0a87ffccba58891fe78543926b1f2142ad --- /dev/null +++ b/alib2common/test-src/core/ComponentsTest.cpp @@ -0,0 +1,124 @@ +#include "ComponentsTest.h" +#include <core/components.hpp> + +CPPUNIT_TEST_SUITE_NAMED_REGISTRATION ( ComponentsTest, "alphabet" ); +CPPUNIT_TEST_SUITE_REGISTRATION ( ComponentsTest ); + +struct GeneralAlphabet { +}; + +struct NonlinearAlphabet { +}; + +struct SubtreeWildcard { +}; + +class A : public std::Components < A, std::string, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < SubtreeWildcard > > { +public: + A ( std::string string ) : std::Components < A, std::string, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < SubtreeWildcard > > ( std::make_tuple ( std::set < std::string > { string, "aaa" }, std::set < std::string > { "aaa" } ), std::make_tuple ( string ) ) { + } +}; + +namespace std { + +template < > +bool A::Set < A, std::string, GeneralAlphabet >::used ( const std::string & string ) const { + return static_cast < const A * > ( this )->accessSet < NonlinearAlphabet > ( ).get ( ).count ( string ) || static_cast < const A * > ( this )->accessElement < SubtreeWildcard > ( ).get ( ) == string; +} + +template < > +bool A::Set < A, std::string, NonlinearAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool A::Set < A, std::string, GeneralAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +bool A::Set < A, std::string, NonlinearAlphabet >::available ( const std::string & string ) const { + return static_cast < const A * > ( this )->accessSet < GeneralAlphabet > ( ).get ( ).count ( string ); +} + +template < > +bool A::Element < A, std::string, SubtreeWildcard >::available ( const std::string & string ) const { + return static_cast < const A * > ( this )->accessSet < GeneralAlphabet > ( ).get ( ).count ( string ); +} + +template < > +void A::Set < A, std::string, GeneralAlphabet >::valid ( const std::string & ) const { +} + +template < > +void A::Set < A, std::string, NonlinearAlphabet >::valid ( const std::string & string ) const { + if ( static_cast < const A * > ( this )->accessElement < SubtreeWildcard > ( ).get ( ) == string ) + throw::exception::CommonException ( "Symbol " + ( std::string ) string + "cannot be set as nonlinear variable since it is already a subtree wildcard" ); +} + +template < > +void A::Element < A, std::string, SubtreeWildcard >::valid ( const std::string & string ) const { + if ( static_cast < const A * > ( this )->accessSet < NonlinearAlphabet > ( ).get ( ).count ( string ) ) + throw::exception::CommonException ( "Symbol " + ( std::string ) string + "cannot be set as subtree wildcard since it is already a nonlinear variable" ); +} + +} + +class B : public std::Components < A, std::string, std::tuple < GeneralAlphabet, NonlinearAlphabet >, std::tuple < > > { +}; + +namespace std { + +template < > +bool B::Set < B, std::string, GeneralAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool B::Set < B, std::string, NonlinearAlphabet >::used ( const std::string & ) const { + return false; +} + +template < > +bool B::Set < B, std::string, GeneralAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +bool B::Set < B, std::string, NonlinearAlphabet >::available ( const std::string & ) const { + return true; +} + +template < > +void B::Set < B, std::string, GeneralAlphabet >::valid ( const std::string & ) const { +} + +template < > +void B::Set < B, std::string, NonlinearAlphabet >::valid ( const std::string & ) const { +} + +} + +void ComponentsTest::setUp ( ) { +} + +void ComponentsTest::tearDown ( ) { +} + +void ComponentsTest::testAdd ( ) { + A tmp ( "2" ); + + CPPUNIT_ASSERT_THROW ( tmp.accessSet < NonlinearAlphabet > ( ).add ( "1" ), exception::CommonException ); + tmp.accessSet < GeneralAlphabet > ( ).add ( "1" ); + tmp.accessSet < NonlinearAlphabet > ( ).add ( "1" ); +} + +void ComponentsTest::testRemove ( ) { + A tmp ( "2" ); + + tmp.accessSet < GeneralAlphabet > ( ).add ( "1" ); + tmp.accessSet < NonlinearAlphabet > ( ).add ( "1" ); + CPPUNIT_ASSERT_THROW ( tmp.accessSet < GeneralAlphabet > ( ).remove ( "1" ), exception::CommonException ); + tmp.accessSet < NonlinearAlphabet > ( ).remove ( "1" ); + tmp.accessSet < GeneralAlphabet > ( ).remove ( "1" ); +} diff --git a/alib2common/test-src/core/ComponentsTest.h b/alib2common/test-src/core/ComponentsTest.h new file mode 100644 index 0000000000000000000000000000000000000000..eb19334635f10f110d24c3ef2a32f47aeb69cf62 --- /dev/null +++ b/alib2common/test-src/core/ComponentsTest.h @@ -0,0 +1,20 @@ +#ifndef ALPHABET_TEST_H_ +#define ALPHABET_TEST_H_ + +#include <cppunit/extensions/HelperMacros.h> + +class ComponentsTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE ( ComponentsTest ); + CPPUNIT_TEST ( testAdd ); + CPPUNIT_TEST ( testRemove ); + CPPUNIT_TEST_SUITE_END ( ); + +public: + void setUp ( ); + void tearDown ( ); + + void testAdd ( ); + void testRemove ( ); +}; + +#endif // ALPHABET_TEST_H_