From 037a10228c022fe1ea228ce6f6f0d7824305a383 Mon Sep 17 00:00:00 2001 From: Richard Underhill Date: Fri, 21 Mar 2014 14:05:18 +0000 Subject: [PATCH] Implementation of boost::any container substitute [Issue#] N/A [Problem] [Cause] [Solution] Change-Id: I8a6ec38bc0c3932f90aff6b5debb972f2fb9fa9a Signed-off-by: Richard Underhill --- automated-tests/dali-test-suite/common/.gitignore | 1 + automated-tests/dali-test-suite/common/file.list | 1 + automated-tests/dali-test-suite/common/tslist | 1 + .../dali-test-suite/common/utc-Dali-Any.cpp | 264 ++++++++++++++ capi/dali/public-api/dali-core-capi-internal.h | 1 + capi/dali/public-api/file.list | 1 + capi/dali/public-api/object/any.h | 403 +++++++++++++++++++++ capi/dali_doc.h | 1 + dali/public-api/file.list | 1 + dali/public-api/object/any.cpp | 87 +++++ 10 files changed, 761 insertions(+) create mode 100644 automated-tests/dali-test-suite/common/utc-Dali-Any.cpp create mode 100644 capi/dali/public-api/object/any.h create mode 100644 dali/public-api/object/any.cpp diff --git a/automated-tests/dali-test-suite/common/.gitignore b/automated-tests/dali-test-suite/common/.gitignore index b92c5ef..1766cee 100644 --- a/automated-tests/dali-test-suite/common/.gitignore +++ b/automated-tests/dali-test-suite/common/.gitignore @@ -1,2 +1,3 @@ utc-Dali-LocklessBuffer utc-Dali-Vector +utc-Dali-Any diff --git a/automated-tests/dali-test-suite/common/file.list b/automated-tests/dali-test-suite/common/file.list index ff93888..b5ebacf 100644 --- a/automated-tests/dali-test-suite/common/file.list +++ b/automated-tests/dali-test-suite/common/file.list @@ -1,3 +1,4 @@ TARGETS += \ utc-Dali-LocklessBuffer \ utc-Dali-Vector \ + utc-Dali-Any \ diff --git a/automated-tests/dali-test-suite/common/tslist b/automated-tests/dali-test-suite/common/tslist index c3dd165..81d0d6d 100644 --- a/automated-tests/dali-test-suite/common/tslist +++ b/automated-tests/dali-test-suite/common/tslist @@ -1,2 +1,3 @@ /dali-test-suite/common/utc-Dali-LocklessBuffer /dali-test-suite/common/utc-Dali-Vector +/dali-test-suite/common/utc-Dali-Any diff --git a/automated-tests/dali-test-suite/common/utc-Dali-Any.cpp b/automated-tests/dali-test-suite/common/utc-Dali-Any.cpp new file mode 100644 index 0000000..b5d7442 --- /dev/null +++ b/automated-tests/dali-test-suite/common/utc-Dali-Any.cpp @@ -0,0 +1,264 @@ +/// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.0 (the License); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://floralicense.org/license/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include +#include + +#include + +#include + +using namespace Dali; + +static void Startup(); +static void Cleanup(); + +extern "C" { + void (*tet_startup)() = Startup; + void (*tet_cleanup)() = Cleanup; +} + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +#define MAX_NUMBER_OF_TESTS 10000 +extern "C" { + struct tet_testlist tet_testlist[MAX_NUMBER_OF_TESTS]; +} + +TEST_FUNCTION( UtcDaliAnyConstructors, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAnyAssignmentOperators, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAnyNegativeAssignmentOperators, NEGATIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAnyGetType, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAnyGet, POSITIVE_TC_IDX ); +TEST_FUNCTION( UtcDaliAnyNegativeGet, NEGATIVE_TC_IDX ); + +// Called only once before first test is run. +static void Startup() +{ +} + +// Called only once after last test is run +static void Cleanup() +{ +} + +static void UtcDaliAnyConstructors() +{ + TestApplication application; + + tet_infoline("Test Any constructors."); + + // Test default constructor. + Any value; + + DALI_TEST_CHECK( typeid( void ) == value.GetType() ); + + // Test constructor Any( const Type& ) + Any value1 = 4u; + + // Test constructor Any( const Any& ) + Any value2 = value1; + + // Test constructor Any( const Any& ) with a non initialized Any + Any value3 = value; + + DALI_TEST_CHECK( typeid( unsigned int ) == value1.GetType() ); + DALI_TEST_CHECK( typeid( unsigned int ) == value2.GetType() ); + DALI_TEST_CHECK( typeid( void ) == value3.GetType() ); + + unsigned int uiValue1 = 0u; + unsigned int uiValue2 = 0u; + value1.Get( uiValue1 ); + value2.Get( uiValue2 ); + + DALI_TEST_EQUALS( uiValue1, uiValue2, TEST_LOCATION ); +} + +static void UtcDaliAnyAssignmentOperators() +{ + TestApplication application; + + tet_infoline("Test assignment operators."); + + float fValue = 0.f; + + Any value1; + + value1 = 4.f; // Test operator=( const Type& ) when current object is not initialized. + + value1.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 4.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + Any value2 = 0.f; + + value2 = 9.f; // Test operator=( const Type& ). + + value2.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 9.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + Any value3 = 5.f; + + value1 = value3; // Test operator=( const Any& ). + + value1.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + Any value4; + + value4 = value3; // Test operator=( const Any& ) when current object is not initialized. + + value4.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + // Test assign a value to value3 doesn't modify value1. + value3 = 3.f; + + value1.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + value3.Get( fValue ); + + DALI_TEST_EQUALS( fValue, 3.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + // Test the branch when copying the same object. + Any value5 = 3.f; + Any& value6( value5 ); + + value6 = value5; + + value6.Get( fValue ); + DALI_TEST_EQUALS( fValue, 3.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliAnyNegativeAssignmentOperators() +{ + TestApplication application; + + tet_infoline("Test assignment operators."); + + Any value1 = 4.f; + Any value2 = 5u; + + bool assert = false; + + try + { + value1 = value2; // Test operator=( const Any& ); + } + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + assert = true; + } + + if( assert ) + { + tet_result( TET_PASS ); + } + else + { + tet_result( TET_FAIL ); + } +} + +static void UtcDaliAnyGetType() +{ + TestApplication application; + + tet_infoline("Test GetType()."); + + Any value; + + DALI_TEST_CHECK( typeid( void ) == value.GetType() ); + + value = 5.f; + + DALI_TEST_CHECK( typeid( float ) == value.GetType() ); +} + +static void UtcDaliAnyGet() +{ + TestApplication application; + + tet_infoline("Test Get()."); + + Any value1( 5.f ); + + float fValue = value1.Get(); + + DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); + + fValue = 0.f; + value1.Get( fValue ); + DALI_TEST_EQUALS( fValue, 5.f, Math::MACHINE_EPSILON_1000, TEST_LOCATION ); +} + +static void UtcDaliAnyNegativeGet() +{ + TestApplication application; + tet_infoline("Test Get()."); + + Any value1; + Any value2( 5.f ); + + bool assert1 = false; + bool assert2 = false; + + unsigned int uiValue = 0u; + + try + { + uiValue = value1.Get< unsigned int >(); + } + + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + assert1 = true; + } + + try + { + uiValue = value2.Get< unsigned int >(); + } + + catch( Dali::DaliException& e ) + { + tet_printf( "Assertion %s failed at %s\n", e.mCondition.c_str(), e.mLocation.c_str() ); + assert2 = true; + } + + if( assert1 && assert2 ) + { + tet_result( TET_PASS ); + } + else + { + tet_result( TET_FAIL ); + } + uiValue++; // supresss warning from unused variable +} + diff --git a/capi/dali/public-api/dali-core-capi-internal.h b/capi/dali/public-api/dali-core-capi-internal.h index 3a99f22..1fba8fe 100644 --- a/capi/dali/public-api/dali-core-capi-internal.h +++ b/capi/dali/public-api/dali-core-capi-internal.h @@ -88,6 +88,7 @@ #include +#include #include #include #include diff --git a/capi/dali/public-api/file.list b/capi/dali/public-api/file.list index 2df2ff0..eb96160 100644 --- a/capi/dali/public-api/file.list +++ b/capi/dali/public-api/file.list @@ -84,6 +84,7 @@ capi_devel_modeling_header_files = \ $(capi_devel_src_dir)/modeling/material.h capi_devel_object_header_files = \ + $(capi_devel_src_dir)/object/any.h \ $(capi_devel_src_dir)/object/base-handle.h \ $(capi_devel_src_dir)/object/base-object.h \ $(capi_devel_src_dir)/object/constrainable.h \ diff --git a/capi/dali/public-api/object/any.h b/capi/dali/public-api/object/any.h new file mode 100644 index 0000000..2f49a21 --- /dev/null +++ b/capi/dali/public-api/object/any.h @@ -0,0 +1,403 @@ +#ifndef __DALI_ANY_TYPE_H__ +#define __DALI_ANY_TYPE_H__ + +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.0 (the License); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://floralicense.org/license/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +/** + * @addtogroup CAPI_DALI_OBJECT_MODULE + * @{ + */ + +// EXTERNAL INCLUDES +#include // operator typeid + + +// INTERNAL INCLUDES +#include + +namespace Dali DALI_IMPORT_API +{ + +/** + * Stores a value of any type. + * + * @note Assignments of values with different types are now allowed to replicate Any behaviour + * + * Examples of use: + * \code{.cpp} + * Any uintVariable = 5u; + * Any floatVariable( 4.5f ); + * Any strVariable( std::string( "Hello world" ) ); + * uintVariable = 1u; + * unsigned int variable = AnyCast< unsigned int >( uintVariable ); + * if ( typeid( int ) == uintVariable.GetType() ) + * \endcode + */ +class Any +{ +public: + /** + * Default constructor. + */ + Any(); + + /** + * Destructor. Free resources. + */ + ~Any(); + +/** + * @brief Pass Assert message + * + * @param assertMessage Assert message to report + */ + static void AssertAlways( const char* assertMessage ); + + /** + * @brief Constructs a Any type with the given value. + * + * @param[in] value The given value. + */ + template + Any( const Type& value ) + : mContainer( new AnyContainerImpl( value ) ) + { + } + + /** + * Copy Constructor. + * @param [in] any Any to be copied. + */ + Any( const Any& any ) + { + // If container isn't empty then copy the container? + if ( NULL != any.mContainer ) + { + mContainer = any.mContainer->mCloneFunc( *any.mContainer ); + } + else + { + // Otherwise mark new container as empty + mContainer = NULL; + } + } + + /** + * @brief Assigns a given value to the Any type. + * + * @note If the types are different, then the current container will be re-created. + * + * @param[in] value The given value. + */ + template + Any& operator=( const Type& value ) + { + + // If the container is empty then assign the new value + if ( NULL == mContainer ) + { + mContainer = new AnyContainerImpl< Type >( value ); + } + else + { + // Check to see if this type is compatible with current container? + if ( mContainer->GetType() == typeid( Type ) ) + { + // Same type so just set the new value + static_cast< AnyContainerImpl< Type >* >( mContainer )->SetValue( value ); + } + else + { + // Incompatible types, so delete old container and assign a new one with this type and value + mContainer->mDeleteFunc( mContainer ); + mContainer = new AnyContainerImpl< Type >( value ); + } + } + return *this; + } + + /** + * @brief Assignment operator. + * + * @note Asserts if values of different types are assigned. + * + * @param [in] any Any to be assigned. + */ + Any& operator=( const Any& any ); + + /** + * @brief Get a value of type Type from container + * + * @param type destination of type Type to write to + */ + template + void Get( Type& type ) + { + type = Get(); + } + + /** + * @brief Returns the type info of the stored value. + * + * @note Returns the info of the void type if there is no value stored. + */ + const std::type_info& GetType() const; + + /** + * @brief Retrieves the stored value in the Any type. + * + * @return The stored value. + */ + template + const Type& Get() const + { + + if ( NULL == mContainer ) + { + AssertAlways( "Any::Get(). mContainer is NULL" ); + } + + // Check if the value has the same value than the Any type. + if( mContainer->GetType() != typeid( Type ) ) + { + AssertAlways( "Any::Get(). Trying to retrieve a value of a different type than the template one." ); + } + return static_cast< AnyContainerImpl< Type >* >( mContainer )->GetValue(); + } + + /** + * @brief Return pointer of Type to the value stored + * + * @return pointer to the value or NULL if no value is contained + */ + template + Type* GetPointer() + { + if( NULL == mContainer ) + { + return NULL; + } + // Check if the value has the same value than the Any type. + if( mContainer->GetType() != typeid( Type ) ) + { + AssertAlways( "Any::GetPointer(). Trying to retrieve a pointer to a value of a different type than the template one." ); + } + return static_cast< AnyContainerImpl< Type >* >( mContainer )->GetPointerToValue(); + } + + /** + * @brief Returns whether container holds a value + * + * @return true or false + */ + bool Empty() + { + return ( NULL == mContainer ) ? true : false; + } + + struct AnyContainerBase; // Forward declaration for typedef + typedef AnyContainerBase* (*CloneFunc)( const AnyContainerBase& base ); + typedef void (*DeleteFunc)( const AnyContainerBase* base ); + + /** + * Base container to hold type for match verification and instance cloning function + * + */ + struct AnyContainerBase + { + /** + * @brief Constructor of base container + * + * @param type typeid of container + * @param cloneFunc Cloning function to replicate this container type + * @param deleteFunc Deleting function to destroy this container type + */ + AnyContainerBase( const std::type_info& type, CloneFunc cloneFunc, DeleteFunc deleteFunc ) + : mType( type ), + mCloneFunc( cloneFunc ), + mDeleteFunc( deleteFunc ) + {} + + /** + * @brief Get the typeid of this container + * + * @return type + */ + const std::type_info& GetType() const + { + return mType; + } + + const::std::type_info& mType; // typeID + CloneFunc mCloneFunc; // cloning function for this container + DeleteFunc mDeleteFunc; // deleting function for this container + }; + + + /** + * @brief Templated Clone function from container base + * + * @param base reference to container + */ + template + struct AnyContainerImplCloner + { + static AnyContainerBase* Clone( const AnyContainerBase& base ) + { + return new AnyContainerImpl< Type >( static_cast< AnyContainerImpl< Type > >( base ) ); + } + }; + + /** + * @brief Templated Delete function from container base + * + * @param base pointer to container + */ + template + struct AnyContainerImplDelete + { + static void Delete( const AnyContainerBase* base ) + { + delete ( static_cast< const AnyContainerImpl< Type >* > ( base ) ); + } + }; + + /** + * @brief Templated class to hold value for type + * + */ + template + class AnyContainerImpl : public AnyContainerBase + { + public: + + /** + * @brief Constructor to create container holding value of type Type + * + * @param value Value of Type + */ + AnyContainerImpl( const Type& value ) + : AnyContainerBase( typeid( Type ), + static_cast< CloneFunc >( &AnyContainerImplCloner< Type >::Clone ), + static_cast< DeleteFunc >( &AnyContainerImplDelete< Type >::Delete ) ), + mValue( value ) + {} + + /** + * @brief Constructor to create new container of type from and existing container (cloning) + * + * @param base reference to base container to copy from + */ + AnyContainerImpl( const AnyContainerBase& base ) + : AnyContainerBase( typeid( Type ), + static_cast< CloneFunc >( &AnyContainerImplCloner< Type >::Clone ), + static_cast< DeleteFunc >( &AnyContainerImplDelete< Type >::Delete ) ) + { + mValue = static_cast< const AnyContainerImpl& >( base ).GetValue(); + } + + /** + * @brief Get the container's stored value + * + * @return value of Type + */ + const Type& GetValue() const + { + return mValue; + } + + void SetValue( const Type& value ) + { + mValue = value; + } + + /** + * @brief Get a pointer to the value held + * + * @return pointer to the value of type Type + */ + Type* GetPointerToValue() + { + return static_cast< Type* >( &mValue ); + } + + private: + Type mValue; + }; + + AnyContainerBase* mContainer; + +}; + +/** + * AnyCast helper functions ( replicates boost functionality, but without exception generation ) + */ + +/** + * @brief Extract a pointer to the held type of an Any object from a pointer to that Any object (NULL if empty ) + * + * @param any Pointer to an Any object + * + * @return Pointer to the Type held + */ +templateinline Type* AnyCast( Any* any ) +{ + return any->GetPointer(); +} + +/** + * @brief Extract a const pointer to the held type of an Any object from a pointer to that Any object (NULL if empty ) + * + * @param any const Pointer to an Any object + * + * @return const Pointer to the Type held + */ +templateinline const Type* AnyCast( const Any* any ) +{ + return any->GetPointer(); +} + +/** + * @brief Extract a held value of type Type from an Any object from a reference to that Any object + * + * @param any reference to an Any object + * + * @return Type value of type Type + */ +templateinline Type AnyCast( Any& any ) +{ + return any.Get(); +} + +/** + * @brief Extract a held value of type Type from an Any object from a const reference to that Any object + * + * @param any reference to an Any object + * + * @return Type value of type Type + */ +templateinline Type AnyCast( const Any& any ) +{ + return any.Get(); +} + +} // namespace Dali + +/** + * @} + */ +#endif // __DALI_ANY_TYPE_H__ diff --git a/capi/dali_doc.h b/capi/dali_doc.h index 64eae78..ea74293 100644 --- a/capi/dali_doc.h +++ b/capi/dali_doc.h @@ -253,6 +253,7 @@ Dali::ShortestDistanceInDomain() * @ref Dali::Property::Value A value-type representing a property value.a * @ref Dali::RefObject Base class for reference counted objects. * @ref Dali::IntrusivePtr Template class used to point at Dali::RefObjects + * @ref Dali::Any Class for objects that contain values of different types * */ diff --git a/dali/public-api/file.list b/dali/public-api/file.list index ff48347..1e8d8c9 100644 --- a/dali/public-api/file.list +++ b/dali/public-api/file.list @@ -79,6 +79,7 @@ public_api_src_files = \ $(public_api_src_dir)/modeling/material.cpp \ $(public_api_src_dir)/modeling/model.cpp \ $(public_api_src_dir)/modeling/model-data.cpp \ + $(public_api_src_dir)/object/any.cpp \ $(public_api_src_dir)/object/base-handle.cpp \ $(public_api_src_dir)/object/constrainable.cpp \ $(public_api_src_dir)/object/handle.cpp \ diff --git a/dali/public-api/object/any.cpp b/dali/public-api/object/any.cpp new file mode 100644 index 0000000..d71d575 --- /dev/null +++ b/dali/public-api/object/any.cpp @@ -0,0 +1,87 @@ +// +// Copyright (c) 2014 Samsung Electronics Co., Ltd. +// +// Licensed under the Flora License, Version 1.0 (the License); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://floralicense.org/license/ +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an AS IS BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +// CLASS HEADER +#include + +// EXTERNAL HEADERS +#include + +namespace Dali +{ + +Any::Any() +: mContainer( NULL ) +{ +} + +Any::~Any() +{ + // Call the implementation deletion function, which will invalidate mContainer + + if ( NULL != mContainer ) + { + mContainer->mDeleteFunc( mContainer ); + mContainer = NULL; + } +} + +Any& Any::operator=( const Any& any ) +{ + if( &any != this ) + { + if( NULL == any.mContainer ) + { + delete mContainer; + mContainer = NULL; + } + else + { + AnyContainerBase* tmp = mContainer; + + if( NULL != mContainer ) + { + // Check if two Any types have the same type. Avoids assignments of values with different types. + if( mContainer->GetType() != any.GetType() ) + { + AssertAlways( "Any::operator=( const Any& Any ). Trying to assign two values with different types." ); + } + } + + // Clone the correct templated object + mContainer = any.mContainer->mCloneFunc( *any.mContainer ); + + // Deletes previous container. + delete tmp; + } + } + + return *this; +} + +const std::type_info& Any::GetType() const +{ + return mContainer ? mContainer->GetType() : typeid( void ); +} + +void Any::AssertAlways( const char* assertMessage ) +{ + DALI_LOG_ERROR_NOFN( assertMessage ); + throw Dali::DaliException( assertMessage, "" ); +} + + +} //namespace Dali -- 2.7.4