diff --git a/.gitmodules b/.gitmodules index 19d788d..37e8830 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "cmake/modules"] path = cmake/modules - url = https://git.bergmann89.de/cpp/CmakeModules.git + url = b3rgmann@git.bergmann89.de:cpp/CMakeModules.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e093f2..21bcf13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,63 +1,63 @@ # Initialize CMake ################################################################################ -CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) +CMake_Minimum_Required ( VERSION 3.12.0 FATAL_ERROR ) # Set CMAKE_BUILD_TYPE -If ( NOT CMAKE_BUILD_TYPE ) - Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) -EndIf ( NOT CMAKE_BUILD_TYPE ) -Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) +If ( NOT CMAKE_BUILD_TYPE ) + Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) +EndIf ( NOT CMAKE_BUILD_TYPE ) +Set_Property ( CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release RelWithDebInfo MinSizeRel ) # Set CMAKE_MODULE_PATH -If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) - Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) -EndIf ( ) -If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) - Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) -EndIf ( ) +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/" ) +EndIf ( ) +If ( EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) + Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/cmake" ) +EndIf ( ) # Project ######################################################################################### -Include ( GNUInstallDirs ) -Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-options.cmake ) -Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-const.cmake ) -Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-var.cmake ) -Project ( ${CPPRTTI_PROJECT_NAME} - DESCRIPTION "${CPPRTTI_PROJECT_DESCRIPTION}" - VERSION "${CPPRTTI_VERSION}" ) -Include ( CTest ) +Include ( GNUInstallDirs ) +Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-options.cmake ) +Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-const.cmake ) +Include ( ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-var.cmake ) +Project ( ${CPPRTTI_PROJECT_NAME} + DESCRIPTION "${CPPRTTI_PROJECT_DESCRIPTION}" + VERSION "${CPPRTTI_VERSION}" ) +Include ( CTest ) # Subdirectories -Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) -Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) +Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) # Install -If ( NOT CPPRTTI_HAS_EXPORT - OR NOT CPPRTTI_INSTALL_PACKAGE ) - Return ( ) -EndIf ( ) - -Include ( CMakePackageConfigHelpers ) -Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake" - VERSION ${CPPRTTI_VERSION} - COMPATIBILITY AnyNewerVersion ) -Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake" - @ONLY ) - -Set ( ConfigPackageLocation "${CPPRTTI_INSTALL_DIR_SHARE}/cmake" ) -Install ( EXPORT - cpprtti - NAMESPACE - cpprtti:: - DESTINATION - ${ConfigPackageLocation} ) -Install ( FILES +If ( NOT CPPRTTI_HAS_EXPORT + OR NOT CPPRTTI_INSTALL_PACKAGE ) + Return ( ) +EndIf ( ) + +Include ( CMakePackageConfigHelpers ) +Write_Basic_Package_Version_File ( "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake" + VERSION ${CPPRTTI_VERSION} + COMPATIBILITY AnyNewerVersion ) +Configure_File ( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cpprtti-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake" - "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake" - DESTINATION - ${ConfigPackageLocation} - COMPONENT - Devel ) + @ONLY ) + +Set ( ConfigPackageLocation "${CPPRTTI_INSTALL_DIR_SHARE}/cmake" ) +Install ( EXPORT + cpprtti + NAMESPACE + cpprtti:: + DESTINATION + ${ConfigPackageLocation} ) +Install ( FILES + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel ) diff --git a/README.md b/README.md new file mode 100644 index 0000000..99d6fcb --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# cpprtti + +C++ run time type information library. diff --git a/cmake/Findcppcore.cmake b/cmake/Findcppcore.cmake index 709f0c4..83d78cf 100644 --- a/cmake/Findcppcore.cmake +++ b/cmake/Findcppcore.cmake @@ -25,7 +25,7 @@ If ( NOT ${CHECKOUT_RET} EQUAL 0 ) EndIf ( ) # Include Cloned Repository -Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake ) +Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/cmake/find_local_module.cmake ) FindLocalModule ( cppcore ${CPPCORE_CHECKOUT_DIR} ) # Create Alias Targets diff --git a/cmake/Findcppmp.cmake b/cmake/Findcppmp.cmake index 072a001..33695e6 100644 --- a/cmake/Findcppmp.cmake +++ b/cmake/Findcppmp.cmake @@ -25,7 +25,7 @@ If ( NOT ${CHECKOUT_RET} EQUAL 0 ) EndIf ( ) # Include Cloned Repository -Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake ) +Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/cmake/find_local_module.cmake ) FindLocalModule ( cppmp ${CPPMP_CHECKOUT_DIR} ) # Create Alias Targets diff --git a/cmake/cpprtti-const.cmake b/cmake/cpprtti-const.cmake index 55348d8..170f44f 100644 --- a/cmake/cpprtti-const.cmake +++ b/cmake/cpprtti-const.cmake @@ -1,28 +1,28 @@ # This file contains constant variables that are fixed to this project # Version -Set ( CPPRTTI_VERSION_MAJOR 1 ) -Set ( CPPRTTI_VERSION_MINOR 0 ) -Set ( CPPRTTI_VERSION_PATCH 0 ) -Set ( CPPRTTI_VERSION_BUILD 0 ) -Set ( CPPRTTI_VERSION_HASH "" ) -Set ( CPPRTTI_VERSION_BEHIND 0 ) -Set ( CPPRTTI_VERSION_DIRTY 0 ) +Set ( CPPRTTI_VERSION_MAJOR 1 ) +Set ( CPPRTTI_VERSION_MINOR 0 ) +Set ( CPPRTTI_VERSION_PATCH 0 ) +Set ( CPPRTTI_VERSION_BUILD 0 ) +Set ( CPPRTTI_VERSION_HASH "" ) +Set ( CPPRTTI_VERSION_BEHIND 0 ) +Set ( CPPRTTI_VERSION_DIRTY 0 ) # Names -Set ( CPPRTTI_PROJECT_NAME "cpprtti" ) -Set ( CPPRTTI_PROJECT_DESCRIPTION "A simple hello world library" ) +Set ( CPPRTTI_PROJECT_NAME "cpprtti" ) +Set ( CPPRTTI_PROJECT_DESCRIPTION "C++ run time type information library" ) # Include generated variables for further usage -Include ( ${CMAKE_CURRENT_LIST_DIR}/cpprtti-var.cmake ) +Include ( ${CMAKE_CURRENT_LIST_DIR}/cpprtti-var.cmake ) # Install directories -Set ( CPPRTTI_INSTALL_DIR_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}/${CPPRTTI_NAME}" ) -Set ( CPPRTTI_INSTALL_DIR_LIB "${CMAKE_INSTALL_LIBDIR}" ) -Set ( CPPRTTI_INSTALL_DIR_SHARE "${CMAKE_INSTALL_DATAROOTDIR}/${CPPRTTI_NAME}" ) +Set ( CPPRTTI_INSTALL_DIR_INCLUDE "${CMAKE_INSTALL_INCLUDEDIR}/${CPPRTTI_NAME}" ) +Set ( CPPRTTI_INSTALL_DIR_LIB "${CMAKE_INSTALL_LIBDIR}" ) +Set ( CPPRTTI_INSTALL_DIR_SHARE "${CMAKE_INSTALL_DATAROOTDIR}/${CPPRTTI_NAME}" ) # C Standard -Set ( CMAKE_C_STANDARD 11 ) -Set ( CMAKE_CXX_STANDARD 17 ) -Set ( CMAKE_C_STANDARD_REQUIRED ON ) -Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) +Set ( CMAKE_C_STANDARD 11 ) +Set ( CMAKE_CXX_STANDARD 17 ) +Set ( CMAKE_C_STANDARD_REQUIRED ON ) +Set ( CMAKE_CXX_STANDARD_REQUIRED ON ) diff --git a/cmake/cpprtti-options.cmake b/cmake/cpprtti-options.cmake index de6d324..561398f 100644 --- a/cmake/cpprtti-options.cmake +++ b/cmake/cpprtti-options.cmake @@ -1,23 +1,11 @@ # This file contains options that can be passed to the cmake command -Option ( CPPRTTI_INSTALL_HEADER - "Install headers of cpprtti." - ON ) -Option ( CPPRTTI_INSTALL_STATIC - "Install static library of cpprtti." - ON ) -Option ( CPPRTTI_INSTALL_SHARED - "Install shared library of cpprtti." - ON ) -Option ( CPPRTTI_INSTALL_DEBUG - "Install the stripped debug informations of cpprtti." - OFF ) -Option ( CPPRTTI_INSTALL_PACKAGE - "Install the cmake package of cpprtti." - ON ) -Option ( CPPRTTI_NO_STRIP - "Do not strip debug symbols from binary." - OFF ) -Option ( CPPRTTI_USE_GIT_VERSION - "Read the git tags to get the version of cpprtti" - ON ) +Option ( CPPRTTI_INSTALL_HEADER + "Install headers of cpprtti." + ON ) +Option ( CPPRTTI_INSTALL_PACKAGE + "Install the cmake package of cpprtti." + ON ) +Option ( CPPRTTI_USE_GIT_VERSION + "Read the git tags to get the version of cpprtti" + ON ) diff --git a/cmake/cpprtti-var.cmake b/cmake/cpprtti-var.cmake index 6e3323d..43b956b 100644 --- a/cmake/cpprtti-var.cmake +++ b/cmake/cpprtti-var.cmake @@ -1,26 +1,32 @@ # This file contains generated variables that are needed for the project # Git Version -If ( CPPRTTI_USE_GIT_VERSION ) - Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER ) - If ( HAS_GIT_HELPER ) - GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/.. - CPPRTTI_VERSION_MAJOR - CPPRTTI_VERSION_MINOR - CPPRTTI_VERSION_PATCH - CPPRTTI_VERSION_BUILD - CPPRTTI_VERSION_HASH - CPPRTTI_VERSION_BEHIND - CPPRTTI_VERSION_DIRTY ) - EndIf ( ) -EndIf ( ) +If ( CPPRTTI_USE_GIT_VERSION ) + Include ( git_helper OPTIONAL RESULT_VARIABLE HAS_GIT_HELPER ) + If ( HAS_GIT_HELPER ) + GitGetVersion ( ${CMAKE_CURRENT_LIST_DIR}/.. + CPPRTTI_VERSION_MAJOR + CPPRTTI_VERSION_MINOR + CPPRTTI_VERSION_PATCH + CPPRTTI_VERSION_BUILD + CPPRTTI_VERSION_HASH + CPPRTTI_VERSION_BEHIND + CPPRTTI_VERSION_DIRTY ) + EndIf ( ) +EndIf ( ) # Strings -Set ( CPPRTTI_VERSION_SHORT "${CPPRTTI_VERSION_MAJOR}.${CPPRTTI_VERSION_MINOR}" ) -Set ( CPPRTTI_VERSION "${CPPRTTI_VERSION_SHORT}.${CPPRTTI_VERSION_PATCH}.${CPPRTTI_VERSION_BUILD}" ) -Set ( CPPRTTI_VERSION_COMPLETE "${CPPRTTI_VERSION}" ) -Set ( CPPRTTI_NAME "${CPPRTTI_PROJECT_NAME}-${CPPRTTI_VERSION_SHORT}" ) -Set ( CPPRTTI_OUTPUTNAME "${CPPRTTI_PROJECT_NAME}" ) -If ( CPPRTTI_VERSION_BEHIND ) - Set ( CPPRTTI_VERSION_COMPLETE "${CPPRTTI_VERSION_COMPLETE}+${CPPRTTI_VERSION_BEHIND}" ) -EndIf ( ) +Set ( CPPRTTI_VERSION_SHORT + "${CPPRTTI_VERSION_MAJOR}.${CPPRTTI_VERSION_MINOR}" ) +Set ( CPPRTTI_VERSION + "${CPPRTTI_VERSION_SHORT}.${CPPRTTI_VERSION_PATCH}.${CPPRTTI_VERSION_BUILD}" ) +Set ( CPPRTTI_VERSION_COMPLETE + "${CPPRTTI_VERSION}" ) +Set ( CPPRTTI_NAME + "${CPPRTTI_PROJECT_NAME}-${CPPRTTI_VERSION_SHORT}" ) +Set ( CPPRTTI_OUTPUTNAME + "${CPPRTTI_PROJECT_NAME}" ) +If ( CPPRTTI_VERSION_BEHIND ) + Set ( CPPRTTI_VERSION_COMPLETE + "${CPPRTTI_VERSION_COMPLETE}+${CPPRTTI_VERSION_BEHIND}" ) +EndIf ( ) diff --git a/cmake/modules b/cmake/modules index ebbae4f..94b9877 160000 --- a/cmake/modules +++ b/cmake/modules @@ -1 +1 @@ -Subproject commit ebbae4fbb42d671331b4c6e9d1d142f41dcacc1b +Subproject commit 94b9877d65e46c9d8169ebc46f163d02e4d9dcf3 diff --git a/include/cpprtti.h b/include/cpprtti.h index 3fa7b6c..1dd6391 100644 --- a/include/cpprtti.h +++ b/include/cpprtti.h @@ -4,3 +4,5 @@ #include #include #include + +#include diff --git a/include/cpprtti/misc.h b/include/cpprtti/misc.h index 454543e..94f5f2e 100644 --- a/include/cpprtti/misc.h +++ b/include/cpprtti/misc.h @@ -2,8 +2,10 @@ #include "misc/exception.h" #include "misc/helper.h" +#include "misc/storage.h" #include "misc/variant.h" #include "misc/exception.inl" #include "misc/helper.inl" +#include "misc/storage.inl" #include "misc/variant.inl" diff --git a/include/cpprtti/misc/exception.h b/include/cpprtti/misc/exception.h index a3e8549..82fccd8 100644 --- a/include/cpprtti/misc/exception.h +++ b/include/cpprtti/misc/exception.h @@ -23,11 +23,15 @@ namespace cpprtti /* variant */ variant_error = 2000, variant_is_empty, //!< The variant does not store any value! + variant_is_const, //!< The value stored in the variant is const! variant_type_mismatch, //!< The variant does not store the requested type! variant_invalid_cast, //!< The value stored in the variant could not be cast to the requested type! + variant_convert_to, //!< No known conversion for the passed types. + variant_assign, //!< Variant is not copy assignable. /* class */ class_error = 3000, + class_member_does_not_exist, //!< The requested member does not exist. class_member_already_exists, //!< Unable to create member: Name is already in use! class_type_mismatch, //!< Type does not match the. class_member_not_readable, //!< Member variable is not readable! @@ -72,5 +76,3 @@ namespace cpprtti }; } - -#include "exception.inl" diff --git a/include/cpprtti/misc/helper.h b/include/cpprtti/misc/helper.h index 64423ff..b033901 100644 --- a/include/cpprtti/misc/helper.h +++ b/include/cpprtti/misc/helper.h @@ -7,6 +7,13 @@ namespace cpprtti { using type_id = size_t; + using storage_id = size_t; + + /** + * @brief Get the ID of a certain C++ storage class. + */ + template + inline storage_id get_storage_id(); /** * @brief Get the ID of a certain C++ type. @@ -21,5 +28,3 @@ namespace cpprtti inline const std::string& get_type_name(); } - -#include "helper.inl" diff --git a/include/cpprtti/misc/helper.inl b/include/cpprtti/misc/helper.inl index 9c18426..c3e307d 100644 --- a/include/cpprtti/misc/helper.inl +++ b/include/cpprtti/misc/helper.inl @@ -10,7 +10,11 @@ namespace cpprtti namespace __impl { - inline type_id create_type_id() + struct tag_type_id { }; + struct tag_storage_id { }; + + template + inline type_id create_id() { static type_id value { }; return ++value; @@ -26,10 +30,19 @@ namespace cpprtti } + template + storage_id get_storage_id() + { + using namespace __impl; + static const auto value = create_id(); + return value; + } + template type_id get_type_id() { - static const auto value = __impl::create_type_id(); + using namespace __impl; + static const auto value = create_id(); return value; } diff --git a/include/cpprtti/misc/storage.h b/include/cpprtti/misc/storage.h new file mode 100644 index 0000000..725b928 --- /dev/null +++ b/include/cpprtti/misc/storage.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include "helper.h" + +namespace cpprtti +{ + + struct storage_container + { + private: + using item_ptr_s = std::shared_ptr; + using item_vector = std::vector; + + private: + item_vector _items; + + public: + /** + * @brief Get the requested storage or nullptr if the storage does not exist. + */ + template + T* storage(); + + /** + * @brief Get the requested storage or nullptr if the storage does not exist. + */ + template + T* storage(bool can_create); + + /** + * @brief Create a new storage. + */ + template + T* storage(const std::shared_ptr& item); + + /** + * @brief Get the requested storage or nullptr if the storage does not exist. + */ + template + const T* storage() const; + }; + +} diff --git a/include/cpprtti/misc/storage.inl b/include/cpprtti/misc/storage.inl new file mode 100644 index 0000000..2a191af --- /dev/null +++ b/include/cpprtti/misc/storage.inl @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include "storage.h" + +namespace cpprtti +{ + + /* storage_container */ + + template + T* storage_container::storage() + { + using storage_type = cppmp::decay_t; + auto id = get_storage_id(); + return (id < _items.size()) + ? static_cast(_items[id].get()) + : nullptr; + } + + template + T* storage_container::storage(bool can_create) + { + using storage_type = cppmp::decay_t; + auto id = get_storage_id(); + if ( can_create + && ( id >= _items.size() + || !_items[id])) + { + _items.resize(id + 1); + _items[id] = std::make_shared(); + } + return (id < _items.size()) + ? static_cast(_items[id].get()) + : nullptr; + } + + template + T* storage_container::storage(const std::shared_ptr& item) + { + using storage_type = cppmp::decay_t; + auto id = get_storage_id(); + _items.resize(id + 1); + _items[id] = item; + return (id < _items.size()) + ? static_cast(_items[id].get()) + : nullptr; + } + + template + const T* storage_container::storage() const + { + using storage_type = cppmp::decay_t; + auto id = get_storage_id(); + return (id < _items.size()) + ? static_cast(_items[id].get()) + : nullptr; + } + +} diff --git a/include/cpprtti/misc/variant.h b/include/cpprtti/misc/variant.h index 9097f66..06bf10e 100644 --- a/include/cpprtti/misc/variant.h +++ b/include/cpprtti/misc/variant.h @@ -5,11 +5,13 @@ #include #include "helper.h" -#include "../types/type.h" namespace cpprtti { + struct type; + struct variant; + namespace __impl { @@ -22,18 +24,36 @@ namespace cpprtti struct variant_impl { + public: const type& type; void * data { nullptr }; bool is_const { false }; bool is_reference { false }; + public: /** * @brief Constructor. */ inline variant_impl(const cpprtti::type& p_type); + + /** + * @brief Destructor. + */ + virtual ~variant_impl() = default; + + /** + * @brief Assign a new value. + */ + inline void assign(const variant& v); + + private: + /** + * @brief Actual implementation of the assign method. + */ + virtual void assign_impl(const variant& v) = 0; }; - using variant_impl_ptr_s = std::shared_ptr; + using variant_impl_ptr_s = std::shared_ptr; } @@ -81,7 +101,23 @@ namespace cpprtti /** * @brief Check if the variant contains a value. */ - inline bool empty(); + inline bool empty() const; + + /** + * @brief Convert this variant to another type. + */ + template + inline variant to() const; + + /** + * @brief Convert this variant to another type. + */ + inline variant to(const cpprtti::type& t) const; + + /** + * @brief Assign the value of this variant from another variant. + */ + inline void assign(const variant& v); /** * @brief Check if the variant contains a value. @@ -98,5 +134,3 @@ namespace cpprtti constexpr decltype(auto) make_variant = cppmp::generic_predicate<__impl::variant_builder> { }; } - -#include "variant.inl" diff --git a/include/cpprtti/misc/variant.inl b/include/cpprtti/misc/variant.inl index 622a5f5..53aebf8 100644 --- a/include/cpprtti/misc/variant.inl +++ b/include/cpprtti/misc/variant.inl @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -24,6 +25,44 @@ namespace cpprtti : type(p_type) { } + void variant_impl::assign(const variant& v) + { + if (is_const) + { + throw exception( + error_code::variant_is_const, + "The value stored in the variant is const!"); + } + + if (v.empty()) + { + throw exception( + error_code::variant_is_empty, + "The passed variant does not store any value!"); + } + + if (type.id() != v.type().id()) + { + auto converter = v.type().convert_to(type.id()); + if (!converter) + { + throw exception( + error_code::variant_convert_to, + cppcore::string_builder() + << "No known conversion from type '" + << v.type().name() + << "' to type '" + << type.name() + << "'!"); + } + this->assign_impl((*converter)(v)); + } + else + { + this->assign_impl(v); + } + } + /* variant_builder - default */ template @@ -61,6 +100,11 @@ namespace cpprtti is_const = cppmp::is_const_v>; is_reference = false; } + + void assign_impl(const variant& v) override + { + *static_cast*>(data) = v.get(); + }; }; template @@ -94,6 +138,30 @@ namespace cpprtti is_const = cppmp::is_const_v>; is_reference = true; } + + void assign_impl(const variant& v) override + { + + }; + + template> + cppmp::enable_if_t> + assign_helper(const variant& v) + { + *static_cast*>(data) = v.get(); + } + + template> + cppmp::enable_if_t> + assign_helper(const variant& v) + { + throw exception( + error_code::variant_assign, + cppcore::string_builder() + << "Value of type '" + << type.name() + << "' is not copy assignable!"); + } }; template @@ -121,6 +189,19 @@ namespace cpprtti { return std::forward(x); } }; + /* variant_builder - type, variant (convert_from) */ + + template + struct variant_builder< + cppmp::list, + void + > + { + template + static constexpr decltype(auto) apply(X_registry& p_registry, const type& t, X_args&&... args) + { return make_variant(std::forward(args)...).to(t.id()); } + }; + /* value_extractor - default */ template @@ -234,12 +315,50 @@ namespace cpprtti << ")"); } - return __impl::extract_value(*_impl, cppmp::type_v); + auto& impl = static_cast(*_impl); + return __impl::extract_value(impl, cppmp::type_v); } - bool variant::empty() + bool variant::empty() const { return !static_cast(_impl); } + template + variant variant::to() const + { + using to_type = cppmp::decay_t; + return to(get_type_id()); + } + + variant variant::to(const cpprtti::type& t) const + { + check_impl(); + + auto& type = _impl->type; + if (t.id() == type.id()) + return *this; + + auto* converter = type.convert_to(t.id()); + if (!converter) + { + throw exception( + error_code::variant_is_empty, + cppcore::string_builder() + << "No known conversion from type '" + << type.name() + << "' to type '" + << t.name() + << "'!"); + } + + return (*converter)(*this); + } + + inline void variant::assign(const variant& v) + { + check_impl(); + _impl->assign(v); + } + variant::operator bool() const { return static_cast(_impl); } diff --git a/include/cpprtti/registry.h b/include/cpprtti/registry.h index 8e822ca..32de9d0 100644 --- a/include/cpprtti/registry.h +++ b/include/cpprtti/registry.h @@ -4,6 +4,7 @@ #include #include "types/type.h" +#include "types/type_decorator.h" namespace cpprtti { @@ -88,9 +89,7 @@ namespace cpprtti * @brief Create a new type for the passed C++ type with the passed implementation type. */ template - inline T_impl& create(); + inline type_decorator& create(); }; } - -#include "registry.inl" diff --git a/include/cpprtti/registry.inl b/include/cpprtti/registry.inl index 1604030..0a57971 100644 --- a/include/cpprtti/registry.inl +++ b/include/cpprtti/registry.inl @@ -18,7 +18,8 @@ namespace cpprtti template const auto& registry::get() const { - using impl_type = default_type_t; + using impl_type = default_type_t; + using decorated_type = type_decorator; auto * t = find(); @@ -31,7 +32,7 @@ namespace cpprtti << get_type_name()); } - auto * ret = dynamic_cast(t); + auto * ret = dynamic_cast(t); if (!ret) { throw exception( @@ -47,11 +48,14 @@ namespace cpprtti template auto& registry::get() { + using impl_type = T_impl; + using decorated_type = type_decorator; + auto * t = find(); if (!t) - t = &create(); + t = &create(); - auto * ret = dynamic_cast(t); + auto * ret = dynamic_cast(t); if (!ret) { throw exception( @@ -105,9 +109,11 @@ namespace cpprtti } template - T_impl& registry::create() + type_decorator& registry::create() { - auto impl = std::make_shared(*this); + using decorated_type = type_decorator; + + auto impl = std::make_shared(*this); auto id_it = _id_map.find(impl->id()); if (id_it != _id_map.end()) diff --git a/include/cpprtti/traits/default_type.h b/include/cpprtti/traits/default_type.h index 63533a0..34762bc 100644 --- a/include/cpprtti/traits/default_type.h +++ b/include/cpprtti/traits/default_type.h @@ -10,5 +10,3 @@ namespace cpprtti using default_type_t = typename default_type_trait>::type; } - -#include "default_type.inl" diff --git a/include/cpprtti/types.h b/include/cpprtti/types.h index 8236ddc..4b6d276 100644 --- a/include/cpprtti/types.h +++ b/include/cpprtti/types.h @@ -5,10 +5,12 @@ #include "types/fundamental_type_tpl.h" #include "types/fundamental_type.h" #include "types/member.h" +#include "types/type_decorator.h" #include "types/type.h" #include "types/class_type_tpl.inl" #include "types/class_type.inl" #include "types/fundamental_type_tpl.inl" #include "types/fundamental_type.inl" +#include "types/type_decorator.inl" #include "types/type.inl" diff --git a/include/cpprtti/types/class_type.h b/include/cpprtti/types/class_type.h index 64464a0..21b8be0 100644 --- a/include/cpprtti/types/class_type.h +++ b/include/cpprtti/types/class_type.h @@ -31,13 +31,18 @@ namespace cpprtti /** * @brief Get a map of all registered members. */ - const member_map& members() const; + inline const member& member(const std::string& p_name) const; + + /** + * @brief Get a map of all registered members. + */ + inline const member_map& members() const; /** * @brief Register a new member variable. */ template - auto& add_member_variable( + inline auto& add_member_variable( const std::string& p_name, T_args&&... p_args); @@ -45,11 +50,9 @@ namespace cpprtti * @brief Register a new member method. */ template - auto& add_member_method( + inline auto& add_member_method( const std::string& p_name, T_args&&... p_args); }; } - -#include "class_type.inl" diff --git a/include/cpprtti/types/class_type.inl b/include/cpprtti/types/class_type.inl index 87a2547..a158e08 100644 --- a/include/cpprtti/types/class_type.inl +++ b/include/cpprtti/types/class_type.inl @@ -16,6 +16,22 @@ namespace cpprtti : type(p_registry, p_id, p_name, rtti_type_t::class_type) { } + const member& class_type::member( + const std::string& p_name) const + { + auto it = _members.find(p_name); + if (it == _members.end()) + { + throw exception( + error_code::class_member_does_not_exist, + cppcore::string_builder() + << "Member with the name '" + << p_name + << "' does not exists"); + } + return *it->second; + } + const class_type::member_map& class_type ::members() const { return _members; } diff --git a/include/cpprtti/types/class_type_tpl.h b/include/cpprtti/types/class_type_tpl.h index 84a4b8d..a70a163 100644 --- a/include/cpprtti/types/class_type_tpl.h +++ b/include/cpprtti/types/class_type_tpl.h @@ -21,5 +21,3 @@ namespace cpprtti }; } - -#include "class_type_tpl.inl" diff --git a/include/cpprtti/types/fundamental_type.h b/include/cpprtti/types/fundamental_type.h index 870da4d..869ce11 100644 --- a/include/cpprtti/types/fundamental_type.h +++ b/include/cpprtti/types/fundamental_type.h @@ -19,5 +19,3 @@ namespace cpprtti }; } - -#include "fundamental_type.inl" diff --git a/include/cpprtti/types/fundamental_type_tpl.h b/include/cpprtti/types/fundamental_type_tpl.h index 7f0b10e..3047167 100644 --- a/include/cpprtti/types/fundamental_type_tpl.h +++ b/include/cpprtti/types/fundamental_type_tpl.h @@ -21,5 +21,3 @@ namespace cpprtti }; } - -#include "fundamental_type_tpl.inl" diff --git a/include/cpprtti/types/member/member.h b/include/cpprtti/types/member/member.h index 05030e2..09140c4 100644 --- a/include/cpprtti/types/member/member.h +++ b/include/cpprtti/types/member/member.h @@ -3,6 +3,7 @@ #include #include "../../types/type.h" +#include "../../misc/storage.h" namespace cpprtti { @@ -15,6 +16,7 @@ namespace cpprtti }; struct member + : public storage_container { protected: cpprtti::type& _owner; @@ -52,5 +54,3 @@ namespace cpprtti }; } - -#include "member.inl" diff --git a/include/cpprtti/types/member/member_method.h b/include/cpprtti/types/member/member_method.h index 12480e2..c60cd05 100644 --- a/include/cpprtti/types/member/member_method.h +++ b/include/cpprtti/types/member/member_method.h @@ -3,6 +3,7 @@ #include #include "member.h" +#include "../../misc/variant.h" namespace cpprtti { @@ -42,5 +43,3 @@ namespace cpprtti constexpr decltype(auto) make_member_method = cppmp::generic_predicate<__impl::member_method_builder> { }; } - -#include "member_method.inl" diff --git a/include/cpprtti/types/member/member_method.inl b/include/cpprtti/types/member/member_method.inl index b2b48ef..6703bf7 100644 --- a/include/cpprtti/types/member/member_method.inl +++ b/include/cpprtti/types/member/member_method.inl @@ -3,6 +3,7 @@ #include #include "member_method.h" +#include "../../misc/variant.h" namespace cpprtti { diff --git a/include/cpprtti/types/member/member_variable.h b/include/cpprtti/types/member/member_variable.h index 1186186..6cf3c50 100644 --- a/include/cpprtti/types/member/member_variable.h +++ b/include/cpprtti/types/member/member_variable.h @@ -36,17 +36,17 @@ namespace cpprtti /** * @brief RTTI type of that member represents. */ - const cpprtti::type& type() const; + inline const cpprtti::type& type() const; /** * @brief Check if the variable is readable. */ - bool is_readale() const; + inline bool is_readale() const; /** * @brief Check if the variable is writable. */ - bool is_writable() const; + inline bool is_writable() const; /** * @brief Get the current value of the member. @@ -75,5 +75,3 @@ namespace cpprtti constexpr decltype(auto) make_member_variable = cppmp::generic_predicate<__impl::member_variable_builder> { }; } - -#include "member_variable.inl" diff --git a/include/cpprtti/types/member/member_variable.inl b/include/cpprtti/types/member/member_variable.inl index 01e8604..783d52e 100644 --- a/include/cpprtti/types/member/member_variable.inl +++ b/include/cpprtti/types/member/member_variable.inl @@ -86,7 +86,8 @@ namespace cpprtti static constexpr decltype(auto) is_read_only_v = cppmp::is_const_v> - || cppmp::is_const_v>; + || cppmp::is_const_v> + || !cppmp::is_copy_assignable_v; }; /* setter_info_trait */ diff --git a/include/cpprtti/types/type.h b/include/cpprtti/types/type.h index 3af7f32..1215b4b 100644 --- a/include/cpprtti/types/type.h +++ b/include/cpprtti/types/type.h @@ -1,8 +1,12 @@ #pragma once +#include #include +#include #include +#include +#include namespace cpprtti { @@ -22,12 +26,21 @@ namespace cpprtti * @brief represents any C++ type */ struct type + : public storage_container { + public: + using converter = std::function; + using converter_map = std::map; + + template + friend struct type_decorator; + protected: cpprtti::registry& _registry; //!< Type registry this type belongs to. type_id _id; //!< Unique ID std::string _name; //!< Name of the type. - rtti_type_t _rtti_type; //!< Type specialization + rtti_type_t _rtti_type; //!< Type specialization. + converter_map _converters; //!< Convert this type to another type. public: /** @@ -68,8 +81,28 @@ namespace cpprtti * @brief Get the RTTI type of this type object. */ inline rtti_type_t rtti_type() const; + + /** + * @brief Get a converter that can convert this type to the passed type. + */ + template + inline const converter* convert_to() const; + + /** + * @brief Get a converter that can convert this type to the passed type ID. + */ + inline const converter* convert_to(type_id id) const; + + /** + * @brief Get a converter that can convert the passed type to this type. + */ + template + inline const converter* convert_from() const; + + /** + * @brief Get a converter that can convert the passed type ID to this type. + */ + inline const converter* convert_from(type_id id) const; }; } - -#include "type.inl" diff --git a/include/cpprtti/types/type.inl b/include/cpprtti/types/type.inl index ee2dfa5..1b99e88 100644 --- a/include/cpprtti/types/type.inl +++ b/include/cpprtti/types/type.inl @@ -1,6 +1,7 @@ #pragma once #include "type.h" +#include "../registry.h" namespace cpprtti { @@ -33,4 +34,38 @@ namespace cpprtti rtti_type_t type::rtti_type() const { return _rtti_type; } + template + const type::converter* type::convert_to() const + { + using to_type = cppmp::decay_t; + return convert_to(get_type_id()); + } + + const type::converter* type::convert_to(type_id id) const + { + auto it = _converters.find(id); + return it == _converters.end() + ? nullptr + : &it->second; + } + + template + const type::converter* type::convert_from() const + { + using from_type = cppmp::decay_t; + return convert_from(get_type_id()); + } + + const type::converter* type::convert_from(type_id id) const + { + auto* type = _registry.find(id); + if (!type) + return nullptr; + + auto it = type->_converters.find(this->_id); + return it == type->_converters.end() + ? nullptr + : &it->second; + } + } diff --git a/include/cpprtti/types/type_decorator.h b/include/cpprtti/types/type_decorator.h new file mode 100644 index 0000000..d8837f2 --- /dev/null +++ b/include/cpprtti/types/type_decorator.h @@ -0,0 +1,32 @@ +#pragma once + +#include "type.h" + +namespace cpprtti +{ + + template + struct type_decorator + : public T_base + { + public: + using base_type = T_base; + using converter = typename type::converter; + + public: + using base_type::base_type; + + /** + * @brief Set the converter that can convert this type to the passed type. + */ + template + inline auto& convert_to(const converter& p_converter); + + /** + * @brief Set the converter that can convert the passed type to this type. + */ + template + inline auto& convert_from(const converter& p_converter); + }; + +} diff --git a/include/cpprtti/types/type_decorator.inl b/include/cpprtti/types/type_decorator.inl new file mode 100644 index 0000000..87534c0 --- /dev/null +++ b/include/cpprtti/types/type_decorator.inl @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "type_decorator.h" +#include "../misc/helper.h" + +namespace cpprtti +{ + + /* type_decorator */ + + template + template + auto& type_decorator + ::convert_to(const converter& p_converter) + { + using to_type = cppmp::decay_t; + + auto id = get_type_id(); + this->_converters[id] = p_converter; + + return *this; + } + + template + template + auto& type_decorator + ::convert_from(const converter& p_converter) + { + using from_type = T_from; + + auto& type = this->_registry.template get(); + type._converters[this->_id] = p_converter; + + return *this; + } + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d7062c5..6f20738 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,38 +1,37 @@ # Initialize ###################################################################################### -Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) -Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) -Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) -Find_Package ( cppcore ) -Find_Package ( cppmp ) +Find_Package ( cppmp ) +Find_Package ( cppcore ) -# Object Library ################################################################################## +# Interface Library ############################################################################### -Set ( CPPRTTI_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) -Add_Library ( cpprtti - INTERFACE ) -Target_Include_Directories ( cpprtti - INTERFACE - $ - $ ) -Target_Link_Libraries ( cpprtti - INTERFACE - cppcore - cppmp ) +Set ( CPPRTTI_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) +Add_Library ( cpprtti INTERFACE ) +Target_Include_Directories ( cpprtti + INTERFACE + $ + $ ) +Target_Link_Libraries ( cpprtti + INTERFACE + cppmp + cppcore ) # Install ######################################################################################### -Set ( CPPRTTI_HAS_EXPORT False PARENT_SCOPE ) +Set ( CPPRTTI_HAS_EXPORT False PARENT_SCOPE ) # Header -If ( CPPRTTI_INSTALL_HEADER ) - Set ( CPPRTTI_HAS_EXPORT True PARENT_SCOPE ) - Install ( FILES ${CPPRTTI_INCLUDE_DIR}/cpprtti.h - DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) - Install ( DIRECTORY ${CPPRTTI_INCLUDE_DIR}/cpprtti - DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) - Install ( TARGETS cpprtti - EXPORT cpprtti - DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) -EndIf ( ) +If ( CPPRTTI_INSTALL_HEADER ) + Set ( CPPRTTI_HAS_EXPORT True PARENT_SCOPE ) + Install ( FILES ${CPPRTTI_INCLUDE_DIR}/cpprtti.h + DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) + Install ( DIRECTORY ${CPPRTTI_INCLUDE_DIR}/cpprtti + DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) + Install ( TARGETS cpprtti + EXPORT cpprtti + DESTINATION ${CPPRTTI_INSTALL_DIR_INCLUDE} ) +EndIf ( ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fe20cfb..e00cd5f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,52 +1,59 @@ # Initialize ###################################################################################### -Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) -Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) -Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) + +Find_Package ( Sanitizers QUIET ) # Test ############################################################################################ -Find_Package ( GTest ) -If ( NOT "${GTest_FOUND}" ) - Return ( ) -EndIf ( ) +Find_Package ( GTest ) +If ( NOT "${GTest_FOUND}" ) + Return ( ) +EndIf ( ) -File ( GLOB_RECURSE CPPRTTI_TEST_HEADER_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) -File ( GLOB_RECURSE CPPRTTI_TEST_INLINE_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) -File ( GLOB_RECURSE CPPRTTI_TEST_SOURCE_FILES - RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) +File ( GLOB_RECURSE CPPRTTI_TEST_HEADER_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/*.h ) +File ( GLOB_RECURSE CPPRTTI_TEST_INLINE_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/*.inl ) +File ( GLOB_RECURSE CPPRTTI_TEST_SOURCE_FILES + RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) -ForEach ( FILE IN LISTS CPPRTTI_TEST_SOURCE_FILES ) +ForEach ( FILE IN LISTS CPPRTTI_TEST_SOURCE_FILES ) # add test - Get_Filename_Component ( TEST_DIR ${FILE} DIRECTORY ) - Get_Filename_Component ( TEST_NAME ${FILE} NAME_WE ) - Set ( TEST_NAME "${TEST_DIR}/${TEST_NAME}" ) - String ( REPLACE "\\" "-" TEST_NAME "${TEST_NAME}" ) - String ( REPLACE "/" "-" TEST_NAME "${TEST_NAME}" ) - String ( REPLACE "_" "-" TEST_NAME "${TEST_NAME}" ) - Set ( TEST_NAME "test-${TEST_NAME}" ) - Add_Executable ( ${TEST_NAME} - EXCLUDE_FROM_ALL - ${CPPRTTI_TEST_HEADER_FILES} - ${CPPRTTI_TEST_INLINE_FILES} - ${FILE} ) - Target_Link_Libraries ( ${TEST_NAME} - PUBLIC - cpprtti - GTest::Main ) + Get_Filename_Component ( TEST_DIR ${FILE} DIRECTORY ) + Get_Filename_Component ( TEST_NAME ${FILE} NAME_WE ) + Set ( TEST_NAME "${TEST_DIR}/${TEST_NAME}" ) + String ( REPLACE "\\" "-" TEST_NAME "${TEST_NAME}" ) + String ( REPLACE "/" "-" TEST_NAME "${TEST_NAME}" ) + String ( REPLACE "_" "-" TEST_NAME "${TEST_NAME}" ) + Set ( TEST_NAME "test-${TEST_NAME}" ) + Add_Executable ( ${TEST_NAME} + EXCLUDE_FROM_ALL + ${CPPRTTI_TEST_HEADER_FILES} + ${CPPRTTI_TEST_INLINE_FILES} + ${FILE} ) + Target_Link_Libraries ( ${TEST_NAME} + PUBLIC + cpprtti + GTest::Main ) + + # Sanitizers + If ( Sanitizers_FOUND ) + Add_Sanitizers ( ${TEST_NAME} ) + EndIf ( ) # pedantic - If ( HAS_PEDANTIC ) - Pedantic_Apply_Flags_Target ( ${TEST_NAME} ALL ) - EndIf ( ) + If ( HAS_PEDANTIC ) + Pedantic_Apply_Flags_Target ( ${TEST_NAME} ALL ) + EndIf ( ) # test - If ( HAS_CMAKE_TESTS ) - Add_CMake_Test ( NAME ${TEST_NAME} TARGET ${TEST_NAME} GROUP cpprtti ) - Else ( ) - Add_Test ( NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) - EndIf ( ) -EndForEach ( ) + If ( HAS_CMAKE_TESTS ) + Add_CMake_Test ( NAME ${TEST_NAME} TARGET ${TEST_NAME} GROUP cpprtti ) + Else ( ) + Add_Test ( NAME ${TEST_NAME} COMMAND ${TEST_NAME} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) + EndIf ( ) +EndForEach ( ) diff --git a/test/cpprtti/misc/variant_tests.cpp b/test/cpprtti/misc/variant_tests.cpp index 194fd01..5025128 100644 --- a/test/cpprtti/misc/variant_tests.cpp +++ b/test/cpprtti/misc/variant_tests.cpp @@ -1,5 +1,6 @@ #include -#include + +#include using namespace cpprtti; diff --git a/test/cpprtti/type/class_type_tests.cpp b/test/cpprtti/type/class_type_tests.cpp index 84f349b..131d430 100644 --- a/test/cpprtti/type/class_type_tests.cpp +++ b/test/cpprtti/type/class_type_tests.cpp @@ -1,4 +1,5 @@ #include + #include using namespace cpprtti;