commit 55443c38a4f7e7b8d60c15cf32283bdbecc9bcf4 Author: bergmann Date: Fri Oct 4 23:51:56 2019 +0200 * Initial commit (implemented basic RTTI for C++) diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..19d788d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cmake/modules"] + path = cmake/modules + url = https://git.bergmann89.de/cpp/CmakeModules.git diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..929597e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,6 @@ +{ + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, + "files.insertFinalNewline": true, + "files.eol": "\n" +} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..5e093f2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,63 @@ +# Initialize CMake ################################################################################ + +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 ) + +# 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 ( ) + +# 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 ) + +# Subdirectories +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 + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/cmake/cpprtti-config-version.cmake" + DESTINATION + ${ConfigPackageLocation} + COMPONENT + Devel ) diff --git a/cmake/Findcppcore.cmake b/cmake/Findcppcore.cmake new file mode 100644 index 0000000..709f0c4 --- /dev/null +++ b/cmake/Findcppcore.cmake @@ -0,0 +1,34 @@ +Set ( CPPCORE_REPOSITORY https://git.bergmann89.de/cpp/cppcore.git ) +Set ( CPPCORE_COMMIT master ) +Set ( CPPCORE_CHECKOUT_DIR ${CMAKE_BINARY_DIR}/sources/cppcore ) + +# Clone Repository +If ( NOT EXISTS ${CPPCORE_CHECKOUT_DIR} ) + Execute_Process ( COMMAND + git clone ${CPPCORE_REPOSITORY} ${CPPCORE_CHECKOUT_DIR} + RESULT_VARIABLE + CLONE_RET ) + If ( NOT ${CLONE_RET} EQUAL 0 ) + Return ( ) + EndIf ( ) +EndIf ( ) + +# Checkout Specific Commit +Execute_Process ( COMMAND + git -C ${CPPCORE_CHECKOUT_DIR} -c advice.detachedHead=false checkout origin/${CPPCORE_COMMIT} + COMMAND + git -C ${CPPCORE_CHECKOUT_DIR} submodule update --init --recursive + RESULT_VARIABLE + CHECKOUT_RET ) +If ( NOT ${CHECKOUT_RET} EQUAL 0 ) + Return ( ) +EndIf ( ) + +# Include Cloned Repository +Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake ) +FindLocalModule ( cppcore ${CPPCORE_CHECKOUT_DIR} ) + +# Create Alias Targets +If ( NOT TARGET cppcore::cppcore ) + Add_Library ( cppcore::cppcore ALIAS cppcore ) +EndIf ( ) diff --git a/cmake/Findcppmp.cmake b/cmake/Findcppmp.cmake new file mode 100644 index 0000000..072a001 --- /dev/null +++ b/cmake/Findcppmp.cmake @@ -0,0 +1,34 @@ +Set ( CPPMP_REPOSITORY https://git.bergmann89.de/cpp/cppmp.git ) +Set ( CPPMP_COMMIT master ) +Set ( CPPMP_CHECKOUT_DIR ${CMAKE_BINARY_DIR}/sources/cppmp ) + +# Clone Repository +If ( NOT EXISTS ${CPPMP_CHECKOUT_DIR} ) + Execute_Process ( COMMAND + git clone ${CPPMP_REPOSITORY} ${CPPMP_CHECKOUT_DIR} + RESULT_VARIABLE + CLONE_RET ) + If ( NOT ${CLONE_RET} EQUAL 0 ) + Return ( ) + EndIf ( ) +EndIf ( ) + +# Checkout Specific Commit +Execute_Process ( COMMAND + git -C ${CPPMP_CHECKOUT_DIR} -c advice.detachedHead=false checkout origin/${CPPMP_COMMIT} + COMMAND + git -C ${CPPMP_CHECKOUT_DIR} submodule update --init --recursive + RESULT_VARIABLE + CHECKOUT_RET ) +If ( NOT ${CHECKOUT_RET} EQUAL 0 ) + Return ( ) +EndIf ( ) + +# Include Cloned Repository +Include ( ${CMAKE_CURRENT_LIST_DIR}/modules/find_local_module.cmake ) +FindLocalModule ( cppmp ${CPPMP_CHECKOUT_DIR} ) + +# Create Alias Targets +If ( NOT TARGET cppmp::cppmp ) + Add_Library ( cppmp::cppmp ALIAS cppmp ) +EndIf ( ) diff --git a/cmake/cpprtti-config.cmake b/cmake/cpprtti-config.cmake new file mode 100644 index 0000000..7401461 --- /dev/null +++ b/cmake/cpprtti-config.cmake @@ -0,0 +1,9 @@ +# cpprtti-config.cmake - package configuration file + +# Include ( CMakeFindDependencyMacro ) +# Find_Dependency ( ) + +Include ( FindPackageHandleStandardArgs ) +Set ( ${CMAKE_FIND_PACKAGE_NAME}_CONFIG ${CMAKE_CURRENT_LIST_FILE} ) +Find_Package_Handle_Standard_Args ( cpprtti CONFIG_MODE ) +Include ( "${CMAKE_CURRENT_LIST_DIR}/cpprtti.cmake") diff --git a/cmake/cpprtti-const.cmake b/cmake/cpprtti-const.cmake new file mode 100644 index 0000000..55348d8 --- /dev/null +++ b/cmake/cpprtti-const.cmake @@ -0,0 +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 ) + +# Names +Set ( CPPRTTI_PROJECT_NAME "cpprtti" ) +Set ( CPPRTTI_PROJECT_DESCRIPTION "A simple hello world library" ) + +# Include generated variables for further usage +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}" ) + +# C Standard +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 new file mode 100644 index 0000000..de6d324 --- /dev/null +++ b/cmake/cpprtti-options.cmake @@ -0,0 +1,23 @@ +# 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 ) diff --git a/cmake/cpprtti-var.cmake b/cmake/cpprtti-var.cmake new file mode 100644 index 0000000..6e3323d --- /dev/null +++ b/cmake/cpprtti-var.cmake @@ -0,0 +1,26 @@ +# 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 ( ) + +# 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 ( ) diff --git a/cmake/modules b/cmake/modules new file mode 160000 index 0000000..ebbae4f --- /dev/null +++ b/cmake/modules @@ -0,0 +1 @@ +Subproject commit ebbae4fbb42d671331b4c6e9d1d142f41dcacc1b diff --git a/include/cpprtti.h b/include/cpprtti.h new file mode 100644 index 0000000..3fa7b6c --- /dev/null +++ b/include/cpprtti.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/include/cpprtti/misc.h b/include/cpprtti/misc.h new file mode 100644 index 0000000..454543e --- /dev/null +++ b/include/cpprtti/misc.h @@ -0,0 +1,9 @@ +#pragma once + +#include "misc/exception.h" +#include "misc/helper.h" +#include "misc/variant.h" + +#include "misc/exception.inl" +#include "misc/helper.inl" +#include "misc/variant.inl" diff --git a/include/cpprtti/misc/exception.h b/include/cpprtti/misc/exception.h new file mode 100644 index 0000000..a3e8549 --- /dev/null +++ b/include/cpprtti/misc/exception.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include + +namespace cpprtti +{ + + enum class error_code + { + unknown = -1, + + none = 0, + + /* registry */ + registry_error = 1000, + registry_type_not_found, //!< Unable to find the requested type. + registry_id_already_exsists, //!< Unable to create type: The type ID does already exist! + registry_name_already_exsists, //!< Unable to create type: The type name does already exist! + registry_type_does_not_match, //!< Unable to get type: The implementation of the type does not match! + + /* variant */ + variant_error = 2000, + variant_is_empty, //!< The variant does not store any value! + 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! + + /* class */ + class_error = 3000, + 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! + class_member_not_writable, //!< Member variable is not writable! + }; + + /** + * @brief Get the string representation of the passed error code. + */ + inline std::string get_error_code_str(error_code err); + + /** + * @brief Exception to represent curl errors. + */ + struct exception + : public ::cppcore::exception + { + public: + error_code error; + const std::string detail; + + /** + * @brief Constructor. + */ + inline exception( + error_code p_error); + + /** + * @brief Constructor. + */ + inline exception( + error_code p_error, + const std::string& p_detail); + + protected: + /** + * @brief Print the message of the exception to the passed stream. + * + * @param[in] os Stream to print message of the exception to. + */ + inline void print_message(std::ostream& os) const override; + }; + +} + +#include "exception.inl" diff --git a/include/cpprtti/misc/exception.inl b/include/cpprtti/misc/exception.inl new file mode 100644 index 0000000..2414067 --- /dev/null +++ b/include/cpprtti/misc/exception.inl @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +#include "exception.h" + +namespace cpprtti +{ + + struct error_code_enum_values + { + using enum_type = error_code; + using enum_value_pair_type = std::pair; + using enum_value_vector_type = std::vector; + + static const enum_value_vector_type& get_enum_values() + { + static const enum_value_vector_type values({ + { error_code::unknown, "Unknown error" }, + + { error_code::none, "No error" }, + + /* registry */ + { error_code::registry_error, "Registry error" }, + { error_code::registry_type_not_found, "Unable to find the requested type!" }, + { error_code::registry_id_already_exsists, "Unable to create type: The type ID does already exist!" }, + { error_code::registry_name_already_exsists, "Unable to create type: The type name does already exist!" }, + { error_code::registry_type_does_not_match, "Unable to get type: The implementation of the type does not match!" }, + + /* variant */ + { error_code::variant_error, "Variant error" }, + { error_code::variant_is_empty, "The variant does not store any value!" }, + { error_code::variant_type_mismatch, "The variant does not store the requested type!" }, + { error_code::variant_invalid_cast, "The value stored in the variant could not be cast to the requested type!" }, + + /* class */ + { error_code::class_error, "Class error" }, + { error_code::class_member_already_exists, "Unable to create member: Name is already in use!" }, + { error_code::class_type_mismatch, "Type does not match the." }, + { error_code::class_member_not_readable, "Member variable is not readable!" }, + { error_code::class_member_not_writable, "Member variable is not writable!" }, + }); + return values; + } + }; + + using error_code_enum_conversion_traits = ::cppcore::enum_conversion_traits; + using error_code_enum_conversion = ::cppcore::enum_conversion ; + + std::string get_error_code_str(error_code err) + { return error_code_enum_conversion::to_string(err); } + + /* exception */ + + exception::exception( + error_code p_error) + : cppcore::exception() + , error (p_error) + , detail () + { } + + exception::exception( + error_code p_error, + const std::string& p_detail) + : cppcore::exception(p_detail) + , error (p_error) + , detail (p_detail) + { } + + void exception + ::print_message(std::ostream& os) const + { + os << get_error_code_str(error) + << '(' + << cppcore::convert_cast>(error) + << "): " + << message; + } + +} diff --git a/include/cpprtti/misc/helper.h b/include/cpprtti/misc/helper.h new file mode 100644 index 0000000..64423ff --- /dev/null +++ b/include/cpprtti/misc/helper.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace cpprtti +{ + + using type_id = size_t; + + /** + * @brief Get the ID of a certain C++ type. + */ + template + inline type_id get_type_id(); + + /** + * @brief Get the name of a certain C++ type. + */ + template + inline const std::string& get_type_name(); + +} + +#include "helper.inl" diff --git a/include/cpprtti/misc/helper.inl b/include/cpprtti/misc/helper.inl new file mode 100644 index 0000000..9c18426 --- /dev/null +++ b/include/cpprtti/misc/helper.inl @@ -0,0 +1,43 @@ +#pragma once + +#include + +#include "helper.h" + +namespace cpprtti +{ + + namespace __impl + { + + inline type_id create_type_id() + { + static type_id value { }; + return ++value; + } + + template + inline std::string create_type_name() + { + int status; + auto name = abi::__cxa_demangle(typeid(X).name(), nullptr, nullptr, &status); + return std::string(name ? name : typeid(X).name()); + } + + } + + template + type_id get_type_id() + { + static const auto value = __impl::create_type_id(); + return value; + } + + template + const std::string& get_type_name() + { + static const auto value = __impl::create_type_name(); + return value; + } + +} diff --git a/include/cpprtti/misc/variant.h b/include/cpprtti/misc/variant.h new file mode 100644 index 0000000..9097f66 --- /dev/null +++ b/include/cpprtti/misc/variant.h @@ -0,0 +1,102 @@ +#pragma once + +#include + +#include + +#include "helper.h" +#include "../types/type.h" + +namespace cpprtti +{ + + namespace __impl + { + + /* make_variant_impl */ + + template + struct variant_builder; + + /* variant_impl */ + + struct variant_impl + { + const type& type; + void * data { nullptr }; + bool is_const { false }; + bool is_reference { false }; + + /** + * @brief Constructor. + */ + inline variant_impl(const cpprtti::type& p_type); + }; + + using variant_impl_ptr_s = std::shared_ptr; + + } + + struct variant + { + public: + template + friend struct __impl::variant_builder; + + private: + __impl::variant_impl_ptr_s _impl; //!< Implementation of the variant + + /** + * @brief Constructor. + */ + inline variant(__impl::variant_impl_ptr_s p_impl); + + public: + /** + * @brief Default constructor. + */ + inline variant(); + + /** + * @brief Get type information of the stored value. + */ + inline const type& type() const; + + /** + * @brief Check if the stored type is a reference. + */ + inline bool is_reference() const; + + /** + * @brief Check if the stored type is a constant. + */ + inline bool is_const() const; + + /** + * @brief Get the stored value. + */ + template + inline T get() const; + + /** + * @brief Check if the variant contains a value. + */ + inline bool empty(); + + /** + * @brief Check if the variant contains a value. + */ + inline operator bool() const; + + private: + /** + * @brief Check if the variant has a value assigned, if not throw an exception. + */ + inline void check_impl() const; + }; + + 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 new file mode 100644 index 0000000..622a5f5 --- /dev/null +++ b/include/cpprtti/misc/variant.inl @@ -0,0 +1,256 @@ +#pragma once + +#include + +#include +#include +#include +#include + +#include "variant.h" +#include "exception.h" +#include "../registry.h" + +namespace cpprtti +{ + + namespace __impl + { + + /* variant_impl */ + + variant_impl::variant_impl( + const cpprtti::type& p_type) + : type(p_type) + { } + + /* variant_builder - default */ + + template + struct variant_builder + { + using is_default = cppmp::true_t; + + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_variant(...)!"); } + }; + + /* variant_builder - not lvalue reference */ + + template + struct variant_builder< + cppmp::list, + cppmp::enable_if_t< + cppmp::is_same_v, registry> + && !cppmp::is_lvalue_reference_v + > + > + { + struct impl + : public variant_impl + { + T storage; + + template + inline impl(const cpprtti::type& p_type, X&& x) + : variant_impl (p_type) + , storage (std::forward(x)) + { + data = reinterpret_cast(const_cast*>(&storage)); + is_const = cppmp::is_const_v>; + is_reference = false; + } + }; + + template + static constexpr decltype(auto) apply(X_registry& p_registry, X&& x) + { + return variant(std::make_shared( + p_registry.template get>(), + std::forward(x))); + } + }; + + /* variant_builder - lvalue reference */ + + template + struct variant_builder< + cppmp::list, + cppmp::enable_if_t< + cppmp::is_same_v, registry> + && cppmp::is_lvalue_reference_v + > + > + { + struct impl + : public variant_impl + { + template + inline impl(const cpprtti::type& p_type, X&& x) + : variant_impl(p_type) + { + data = reinterpret_cast(const_cast*>(&x)); + is_const = cppmp::is_const_v>; + is_reference = true; + } + }; + + template + static constexpr decltype(auto) apply(X_registry& p_registry, X&& x) + { + return variant(std::make_shared( + p_registry.template get>(), + std::forward(x))); + } + }; + + /* variant_builder - variant */ + + template + struct variant_builder< + cppmp::list, + cppmp::enable_if_t< + cppmp::is_same_v, registry> + && cppmp::is_same_v, variant> + > + > + { + template + static constexpr decltype(auto) apply(X_registry& p_registry, X&& x) + { return std::forward(x); } + }; + + /* value_extractor - default */ + + template + struct value_extractor + { + using is_default = cppmp::true_t; + + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for variant.get(...)!"); } + }; + + /* value_extractor - T&& */ + + template + struct value_extractor< + cppmp::list< + const variant_impl&, + const cppmp::type_t&>, + void> + { + static constexpr decltype(auto) apply( + const variant_impl& impl, + const cppmp::type_t&) + { + if (impl.is_const) + { + throw exception( + error_code::variant_invalid_cast, + "Can not move constant value"); + } + return std::move(*reinterpret_cast(impl.data)); + } + }; + + /* value_extractor - normal */ + + template + struct value_extractor< + cppmp::list< + const variant_impl&, + const cppmp::type_t&>, + void> + { + static constexpr decltype(auto) apply( + const variant_impl& impl, + const cppmp::type_t&) + { + bool is_const = cppmp::is_const_v>; + bool is_reference = cppmp::is_reference_v; + + if ( is_reference + && !is_const + && impl.is_const) + { + throw exception( + error_code::variant_invalid_cast, + "Can not get a reference to a const value"); + } + + return *reinterpret_cast*>(impl.data); + } + }; + + constexpr decltype(auto) extract_value = cppmp::generic_predicate<__impl::value_extractor> { }; + + } + + /* variant */ + + variant::variant(__impl::variant_impl_ptr_s p_impl) + : _impl(p_impl) + { } + + variant::variant() + : _impl() + { } + + const type& variant::type() const + { + check_impl(); + return _impl->type; + } + + bool variant::is_reference() const + { + check_impl(); + return _impl->is_reference; + } + + bool variant::is_const() const + { + check_impl(); + return _impl->is_const; + } + + template + T variant::get() const + { + check_impl(); + + if (_impl->type.id() != get_type_id>()) + { + throw exception( + error_code::variant_type_mismatch, + cppcore::string_builder() + << "The vaiant does not store the requested type (requested=" + << get_type_name>() + << ", stored=" + << _impl->type.name() + << ")"); + } + + return __impl::extract_value(*_impl, cppmp::type_v); + } + + bool variant::empty() + { return !static_cast(_impl); } + + variant::operator bool() const + { return static_cast(_impl); } + + void variant::check_impl() const + { + if (!_impl) + { + throw exception( + error_code::variant_is_empty, + "The variant does not store any value!"); + } + } + +} diff --git a/include/cpprtti/registry.h b/include/cpprtti/registry.h new file mode 100644 index 0000000..8e822ca --- /dev/null +++ b/include/cpprtti/registry.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include "types/type.h" + +namespace cpprtti +{ + + struct registry + { + public: + using type_ptr_s = std::shared_ptr; + using type_id_map = std::map; + using type_name_map = std::map; + + private: + type_id_map _id_map; + type_name_map _name_map; + + public: + /** + * @brief Get a type from the registry. + * + * This method will return the type for the requested parameter. + * It may create a new type object with the default implementation, + * if the type does not exist. + */ + template + inline auto& get(); + + /** + * @brief Get a type from the registry. + * + * This method will return the type for the requested parameter. + * If the type was not found, an exception is thrown. + */ + template + inline const auto& get() const; + + /** + * @brief Get a type from the registry. + * + * This method will return the type for the requested parameter. + * It may create a new type object with the passed implementation, + * if the type does not exist. + * If the type implementation does not match the requested one, + * an exception is thrown. + */ + template + inline auto& get(); + + /** + * @brief Search a type by it's represented C++ type. + */ + template + inline type * find(); + + /** + * @brief Search a type by it's represented C++ type. + */ + template + inline const type * find() const; + + /** + * @brief Search a type by it's ID. + */ + inline type * find(type_id id); + + /** + * @brief Search a type by it's ID. + */ + inline const type * find(type_id id) const; + + /** + * @brief Search a type by it's name. + */ + inline type * find(const std::string& name); + + /** + * @brief Search a type by it's name. + */ + inline const type * find(const std::string& name) const; + + private: + /** + * @brief Create a new type for the passed C++ type with the passed implementation type. + */ + template + inline T_impl& create(); + }; + +} + +#include "registry.inl" diff --git a/include/cpprtti/registry.inl b/include/cpprtti/registry.inl new file mode 100644 index 0000000..1604030 --- /dev/null +++ b/include/cpprtti/registry.inl @@ -0,0 +1,140 @@ +#pragma once + +#include + +#include "registry.h" +#include "misc/exception.h" +#include "traits/default_type.h" + +namespace cpprtti +{ + + /* registry */ + + template + auto& registry::get() + { return get>(); } + + template + const auto& registry::get() const + { + using impl_type = default_type_t; + + auto * t = find(); + + if (!t) + { + throw exception( + error_code::registry_type_not_found, + cppcore::string_builder() + << "The requested type could not be found: " + << get_type_name()); + } + + auto * ret = dynamic_cast(t); + if (!ret) + { + throw exception( + error_code::registry_type_does_not_match, + cppcore::string_builder() + << "The type does not match the expected implementation: " + << get_type_name()); + } + + return *ret; + } + + template + auto& registry::get() + { + auto * t = find(); + if (!t) + t = &create(); + + auto * ret = dynamic_cast(t); + if (!ret) + { + throw exception( + error_code::registry_type_does_not_match, + cppcore::string_builder() + << "The type does not match the expected implementation: " + << get_type_name()); + } + + return * ret; + } + + template + type * registry::find() + { return find(get_type_id>()); } + + template + const type * registry::find() const + { return find(get_type_id>()); } + + type * registry::find(type_id id) + { + auto it = _id_map.find(id); + return it == _id_map.end() + ? nullptr + : it->second.get(); + } + + const type * registry::find(type_id id) const + { + auto it = _id_map.find(id); + return it == _id_map.end() + ? nullptr + : it->second.get(); + } + + type * registry::find(const std::string& name) + { + auto it = _name_map.find(name); + return it == _name_map.end() + ? nullptr + : it->second.get(); + } + + const type * registry::find(const std::string& name) const + { + auto it = _name_map.find(name); + return it == _name_map.end() + ? nullptr + : it->second.get(); + } + + template + T_impl& registry::create() + { + auto impl = std::make_shared(*this); + + auto id_it = _id_map.find(impl->id()); + if (id_it != _id_map.end()) + { + throw exception( + error_code::registry_id_already_exsists, + cppcore::string_builder() + << "A type with the id '" + << impl->id() + << "' is already registered!"); + } + + auto name_it = _name_map.find(impl->name()); + if (name_it != _name_map.end()) + { + throw exception( + error_code::registry_name_already_exsists, + cppcore::string_builder() + << "A type with the name '" + << impl->name() + << "' is already registered!"); + } + + _id_map.emplace_hint(id_it, impl->id(), impl); + _name_map.emplace_hint(name_it, impl->name(), impl); + + return *impl; + } + +} diff --git a/include/cpprtti/traits.h b/include/cpprtti/traits.h new file mode 100644 index 0000000..c8bbe94 --- /dev/null +++ b/include/cpprtti/traits.h @@ -0,0 +1,5 @@ +#pragma once + +#include "traits/default_type.h" + +#include "traits/default_type.inl" diff --git a/include/cpprtti/traits/default_type.h b/include/cpprtti/traits/default_type.h new file mode 100644 index 0000000..63533a0 --- /dev/null +++ b/include/cpprtti/traits/default_type.h @@ -0,0 +1,14 @@ +#pragma once + +namespace cpprtti +{ + + template + struct default_type_trait; + + template + using default_type_t = typename default_type_trait>::type; + +} + +#include "default_type.inl" diff --git a/include/cpprtti/traits/default_type.inl b/include/cpprtti/traits/default_type.inl new file mode 100644 index 0000000..539604c --- /dev/null +++ b/include/cpprtti/traits/default_type.inl @@ -0,0 +1,27 @@ +#pragma once + +#include "default_type.h" + +#include "../types/class_type_tpl.h" +#include "../types/fundamental_type_tpl.h" + +namespace cpprtti +{ + + template + struct default_type_trait< + T, + std::enable_if_t>> + { + using type = fundamental_type_tpl; + }; + + template + struct default_type_trait< + T, + std::enable_if_t>> + { + using type = class_type_tpl; + }; + +} diff --git a/include/cpprtti/types.h b/include/cpprtti/types.h new file mode 100644 index 0000000..8236ddc --- /dev/null +++ b/include/cpprtti/types.h @@ -0,0 +1,14 @@ +#pragma once + +#include "types/class_type_tpl.h" +#include "types/class_type.h" +#include "types/fundamental_type_tpl.h" +#include "types/fundamental_type.h" +#include "types/member.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.inl" diff --git a/include/cpprtti/types/class_type.h b/include/cpprtti/types/class_type.h new file mode 100644 index 0000000..64464a0 --- /dev/null +++ b/include/cpprtti/types/class_type.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "type.h" +#include "member/member.h" + +namespace cpprtti +{ + + struct class_type + : public type + { + private: + using member_ptr_u = std::unique_ptr; + using member_map = std::map; + + private: + member_map _members; + + public: + /** + * @brief Constructor. + */ + inline class_type( + cpprtti::registry& p_registry, + type_id p_id, + const std::string& p_name); + + /** + * @brief Get a map of all registered members. + */ + const member_map& members() const; + + /** + * @brief Register a new member variable. + */ + template + auto& add_member_variable( + const std::string& p_name, + T_args&&... p_args); + + /** + * @brief Register a new member method. + */ + template + 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 new file mode 100644 index 0000000..87a2547 --- /dev/null +++ b/include/cpprtti/types/class_type.inl @@ -0,0 +1,71 @@ +#pragma once + +#include "class_type.h" +#include "member/member_method.h" +#include "member/member_variable.h" + +namespace cpprtti +{ + + /* class_type */ + + class_type::class_type( + cpprtti::registry& p_registry, + type_id p_id, + const std::string& p_name) + : type(p_registry, p_id, p_name, rtti_type_t::class_type) + { } + + const class_type::member_map& class_type + ::members() const + { return _members; } + + template + auto& class_type + ::add_member_variable( + const std::string& p_name, + T_args&&... p_args) + { + auto it = _members.find(p_name); + if (it != _members.end()) + { + throw exception( + error_code::class_member_already_exists, + cppcore::string_builder() + << "Member with the name '" + << p_name + << "' already exists"); + } + + auto ptr = make_member_variable(*this, p_name, std::forward(p_args)...); + auto& ret = *ptr; + _members.emplace_hint(it, p_name, std::move(ptr)); + + return ret; + } + + template + auto& class_type + ::add_member_method( + const std::string& p_name, + T_args&&... p_args) + { + auto it = _members.find(p_name); + if (it != _members.end()) + { + throw exception( + error_code::class_member_already_exists, + cppcore::string_builder() + << "Member with the name '" + << p_name + << "' already exists"); + } + + auto ptr = make_member_method(*this, p_name, std::forward(p_args)...); + auto& ret = *ptr; + _members.emplace_hint(it, p_name, std::move(ptr)); + + return ret; + } + +} diff --git a/include/cpprtti/types/class_type_tpl.h b/include/cpprtti/types/class_type_tpl.h new file mode 100644 index 0000000..84a4b8d --- /dev/null +++ b/include/cpprtti/types/class_type_tpl.h @@ -0,0 +1,25 @@ +#pragma once + +#include "class_type.h" + +namespace cpprtti +{ + + template + struct class_type_tpl + : public class_type + { + public: + using value_type = T_value; + + public: + /** + * @brief Constructor. + */ + inline class_type_tpl( + cpprtti::registry& p_registry); + }; + +} + +#include "class_type_tpl.inl" diff --git a/include/cpprtti/types/class_type_tpl.inl b/include/cpprtti/types/class_type_tpl.inl new file mode 100644 index 0000000..f4d348e --- /dev/null +++ b/include/cpprtti/types/class_type_tpl.inl @@ -0,0 +1,20 @@ +#pragma once + +#include "class_type_tpl.h" +#include "../misc/helper.h" + +namespace cpprtti +{ + + /* class_type_tpl */ + + template + class_type_tpl::class_type_tpl( + cpprtti::registry& p_registry) + : class_type( + p_registry, + get_type_id(), + get_type_name()) + { } + +} diff --git a/include/cpprtti/types/fundamental_type.h b/include/cpprtti/types/fundamental_type.h new file mode 100644 index 0000000..870da4d --- /dev/null +++ b/include/cpprtti/types/fundamental_type.h @@ -0,0 +1,23 @@ +#pragma once + +#include "type.h" + +namespace cpprtti +{ + + struct fundamental_type + : public type + { + public: + /** + * @brief Constructor. + */ + inline fundamental_type( + cpprtti::registry& p_registry, + size_t p_id, + const std::string& p_name); + }; + +} + +#include "fundamental_type.inl" diff --git a/include/cpprtti/types/fundamental_type.inl b/include/cpprtti/types/fundamental_type.inl new file mode 100644 index 0000000..4269139 --- /dev/null +++ b/include/cpprtti/types/fundamental_type.inl @@ -0,0 +1,17 @@ +#pragma once + +#include "fundamental_type.h" + +namespace cpprtti +{ + + /* fundamental_type */ + + fundamental_type::fundamental_type( + cpprtti::registry& p_registry, + size_t p_id, + const std::string& p_name) + : type(p_registry, p_id, p_name, rtti_type_t::fundamental_type) + { } + +} diff --git a/include/cpprtti/types/fundamental_type_tpl.h b/include/cpprtti/types/fundamental_type_tpl.h new file mode 100644 index 0000000..7f0b10e --- /dev/null +++ b/include/cpprtti/types/fundamental_type_tpl.h @@ -0,0 +1,25 @@ +#pragma once + +#include "fundamental_type.h" + +namespace cpprtti +{ + + template + struct fundamental_type_tpl + : public fundamental_type + { + public: + using value_type = T_value; + + public: + /** + * @brief Constructor. + */ + inline fundamental_type_tpl( + cpprtti::registry& p_registry); + }; + +} + +#include "fundamental_type_tpl.inl" diff --git a/include/cpprtti/types/fundamental_type_tpl.inl b/include/cpprtti/types/fundamental_type_tpl.inl new file mode 100644 index 0000000..2ccba7c --- /dev/null +++ b/include/cpprtti/types/fundamental_type_tpl.inl @@ -0,0 +1,19 @@ +#pragma once + +#include "fundamental_type_tpl.h" + +namespace cpprtti +{ + + /* fundamental_type_tpl */ + + template + fundamental_type_tpl::fundamental_type_tpl( + cpprtti::registry& p_registry) + : fundamental_type( + p_registry, + get_type_id(), + get_type_name()) + { } + +} diff --git a/include/cpprtti/types/member.h b/include/cpprtti/types/member.h new file mode 100644 index 0000000..e16beaf --- /dev/null +++ b/include/cpprtti/types/member.h @@ -0,0 +1,9 @@ +#pragma once + +#include "member/member_method.h" +#include "member/member_variable.h" +#include "member/member.h" + +#include "member/member_method.inl" +#include "member/member_variable.inl" +#include "member/member.inl" diff --git a/include/cpprtti/types/member/member.h b/include/cpprtti/types/member/member.h new file mode 100644 index 0000000..05030e2 --- /dev/null +++ b/include/cpprtti/types/member/member.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "../../types/type.h" + +namespace cpprtti +{ + + enum class member_type + { + unknown = 0, + variable, + method, + }; + + struct member + { + protected: + cpprtti::type& _owner; + std::string _name; + member_type _member_type; + + public: + /** + * @brief Constructor. + */ + inline member( + cpprtti::type& p_owner, + const std::string& p_name, + member_type p_member_type); + + /** + * @brief Destructor. + */ + virtual ~member() = default; + + /** + * @brief Type that owns this member. + */ + inline const cpprtti::type& owner() const; + + /** + * @brief Get the name of the member. + */ + inline const std::string& name() const; + + /** + * @brief Type of the member. + */ + inline cpprtti::member_type member_type() const; + }; + +} + +#include "member.inl" diff --git a/include/cpprtti/types/member/member.inl b/include/cpprtti/types/member/member.inl new file mode 100644 index 0000000..2b0b4f0 --- /dev/null +++ b/include/cpprtti/types/member/member.inl @@ -0,0 +1,28 @@ +#pragma once + +#include "member.h" + +namespace cpprtti +{ + + /* member */ + + member::member( + cpprtti::type& p_owner, + const std::string& p_name, + cpprtti::member_type p_member_type) + : _owner (p_owner) + , _name (p_name) + , _member_type (p_member_type) + { } + + const cpprtti::type& member::owner() const + { return _owner; } + + const std::string& member::name() const + { return _name; } + + member_type cpprtti::member::member_type() const + { return _member_type; } + +} diff --git a/include/cpprtti/types/member/member_method.h b/include/cpprtti/types/member/member_method.h new file mode 100644 index 0000000..12480e2 --- /dev/null +++ b/include/cpprtti/types/member/member_method.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "member.h" + +namespace cpprtti +{ + + namespace __impl + { + + template + struct member_method_builder; + + } + + struct member_method + : public member + { + public: + /** + * @brief Constructor. + */ + inline member_method( + cpprtti::type& p_owner, + const std::string& p_name); + + /** + * @brief Invoke the member method. + */ + template + inline variant invoke(T_object&& obj, T_args&&... args) const; + + protected: + /** + * @brief Invoke the member method. + */ + virtual variant invoke_impl(const variant& obj, const std::vector& args) const = 0; + }; + + 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 new file mode 100644 index 0000000..b2b48ef --- /dev/null +++ b/include/cpprtti/types/member/member_method.inl @@ -0,0 +1,113 @@ +#pragma once + +#include + +#include "member_method.h" + +namespace cpprtti +{ + + /* member_method */ + + member_method::member_method( + cpprtti::type& p_owner, + const std::string& p_name) + : member( + p_owner, + p_name, + cpprtti::member_type::method) + { } + + template + variant member_method + ::invoke(T_object&& obj, T_args&&... args) const + { + return invoke_impl( + make_variant(_owner.registry(), std::forward(obj)), + std::vector({ + make_variant(_owner.registry(), std::forward(args))... + })); + } + + namespace __impl + { + + /* member_method_builder - default */ + + template + struct member_method_builder + { + using is_default = cppmp::true_t; + + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_member_method(...)!"); } + }; + + /* member_method_builder - member method */ + + template + struct member_method_builder< + cppmp::list, + cppmp::enable_if_t< + cppmp::is_valid_v> + && !cppmp::lambda_traits::is_static_v + > + > + { + using method_type = T_method; + using lambda_trait = cppmp::lambda_traits; + using object_type = typename lambda_trait::object_type; + + struct member_impl + : public member_method + { + private: + method_type _member; + + public: + inline member_impl( + cpprtti::type& p_owner, + const std::string& p_name, + method_type p_member) + : member_method(p_owner, p_name) + , _member(p_member) + { + if (_owner.id() != get_type_id>()) + { + throw exception( + error_code::class_type_mismatch, + "Object type of the member method does not match the type of the owner!"); + } + } + + protected: + variant invoke_impl(const variant& obj, const std::vector& args) const override + { return invoke_helper(obj, args, std::make_index_sequence { }); } + + private: + template + inline variant invoke_helper(const variant& obj, const std::vector& args, std::index_sequence) const + { + return make_variant( + _owner.registry(), + (obj.get().*_member)( + args[I].get>()... + ) + ); + } + }; + + template + static constexpr decltype(auto) apply(type& owner, const std::string& name, X_member&& member) + { + return std::make_unique( + owner, + name, + std::forward(member)); + } + }; + + } + +} diff --git a/include/cpprtti/types/member/member_variable.h b/include/cpprtti/types/member/member_variable.h new file mode 100644 index 0000000..1186186 --- /dev/null +++ b/include/cpprtti/types/member/member_variable.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include "member.h" +#include "../../misc/variant.h" + +namespace cpprtti +{ + + namespace __impl + { + + template + struct member_variable_builder; + + } + + struct member_variable + : public member + { + protected: + cpprtti::type& _type; + bool _is_readale { true }; + bool _is_writable { true }; + + public: + /** + * @brief Constructor. + */ + inline member_variable( + cpprtti::type& p_owner, + const std::string& p_name, + cpprtti::type& p_type); + + /** + * @brief RTTI type of that member represents. + */ + const cpprtti::type& type() const; + + /** + * @brief Check if the variable is readable. + */ + bool is_readale() const; + + /** + * @brief Check if the variable is writable. + */ + bool is_writable() const; + + /** + * @brief Get the current value of the member. + */ + template + inline variant get(T_object& obj) const; + + /** + * @brief Set the new value of the member. + */ + template + inline void set(T_object& obj, const T_value& value) const; + + protected: + /** + * @brief Get the current value of the member. + */ + virtual variant get_impl(const variant& obj) const = 0; + + /** + * @brief Set the new value of the member. + */ + virtual void set_impl(const variant& obj, const variant& value) const = 0; + }; + + 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 new file mode 100644 index 0000000..01e8604 --- /dev/null +++ b/include/cpprtti/types/member/member_variable.inl @@ -0,0 +1,387 @@ +#pragma once + +#include +#include + +#include "member_variable.h" + +namespace cpprtti +{ + + /* member_variable */ + + member_variable::member_variable( + cpprtti::type& p_owner, + const std::string& p_name, + cpprtti::type& p_type) + : member( + p_owner, + p_name, + member_type::variable) + , _type(p_type) + { } + + const cpprtti::type& member_variable::type() const + { return _type; } + + bool member_variable::is_readale() const + { return _is_readale; } + + bool member_variable::is_writable() const + { return _is_writable; } + + template + variant member_variable::get(T_object& obj) const + { + if (!_is_readale) + { + throw exception( + error_code::class_member_not_readable, + cppcore::string_builder() + << "Member variable '" + << _name + << "' of '" + << _owner.name() + << " is not readable!"); + } + return get_impl(make_variant(_owner.registry(), obj)); + } + + template + inline void member_variable::set(T_object& obj, const T_value& value) const + { + if (!_is_writable) + { + throw exception( + error_code::class_member_not_writable, + cppcore::string_builder() + << "Member variable '" + << _name + << "' of '" + << _owner.name() + << " is not writable!"); + } + return set_impl(make_variant(_owner.registry(), obj), make_variant(_owner.registry(), value)); + } + + namespace __impl + { + + /* getter_info_trait */ + + template + struct getter_info_trait; + + template + struct getter_info_trait< + T_getter, + cppmp::enable_if_t< + decltype(cppmp::make_getter)::is_valid_v + > + > + { + using getter_type = decltype(cppmp::make_getter(std::declval())); + using object_type = typename getter_type::object_type; + using value_type = typename getter_type::value_type; + + static constexpr decltype(auto) is_read_only_v = + cppmp::is_const_v> + || cppmp::is_const_v>; + }; + + /* setter_info_trait */ + + template + struct setter_info_trait; + + template + struct setter_info_trait< + T_setter, + cppmp::enable_if_t< + decltype(cppmp::make_setter)::is_valid_v + > + > + { + using setter_type = decltype(cppmp::make_setter(std::declval())); + using object_type = typename setter_type::object_type; + using value_type = typename setter_type::value_type; + + static constexpr decltype(auto) is_write_only_v = + !cppmp::is_base_of_v; + }; + + /* member_variable_builder - default */ + + template + struct member_variable_builder + { + using is_default = cppmp::true_t; + + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for make_member_variable(...)!"); } + }; + + /* member_variable_builder - read-write getter */ + + template + struct member_variable_builder< + cppmp::list, + cppmp::enable_if_t< + decltype(cppmp::make_getter)::is_valid_v + && !getter_info_trait::is_read_only_v + > + > + { + using getter_type = cppmp::decay_t()))>; + using object_type = typename getter_type::object_type; + using value_type = typename getter_type::value_type; + + struct member_impl + : public member_variable + { + private: + getter_type _getter; + + public: + inline member_impl( + cpprtti::type& p_owner, + const std::string& p_name, + getter_type p_getter) + : member_variable(p_owner, p_name, p_owner.registry().get()) + , _getter(p_getter) + { + if (_owner.id() != get_type_id>()) + { + throw exception( + error_code::class_type_mismatch, + "Object type of the getter does not match the type of the owner!"); + } + + _is_readale = true; + _is_writable = true; + } + + protected: + variant get_impl(const variant& obj) const override + { + return obj.is_const() + ? make_variant(_owner.registry(), _getter(obj.get())) + : make_variant(_owner.registry(), _getter(obj.get())); + } + + void set_impl(const variant& obj, const variant& value) const override + { _getter(obj.get()) = value.get(); } + }; + + template + static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter) + { + return std::make_unique( + owner, + name, + cppmp::make_getter(std::forward(getter))); + } + }; + + /* member_variable_builder - read-only getter */ + + template + struct member_variable_builder< + cppmp::list, + cppmp::enable_if_t< + decltype(cppmp::make_getter)::is_valid_v + && getter_info_trait::is_read_only_v + > + > + { + using getter_type = cppmp::decay_t()))>; + using object_type = typename getter_type::object_type; + using value_type = typename getter_type::value_type; + + struct member_impl + : public member_variable + { + private: + getter_type _getter; + + public: + inline member_impl( + cpprtti::type& p_owner, + const std::string& p_name, + getter_type p_getter) + : member_variable(p_owner, p_name, p_owner.registry().get()) + , _getter(p_getter) + { + if (_owner.id() != get_type_id>()) + { + throw exception( + error_code::class_type_mismatch, + "Object type of the getter does not match the type of the owner!"); + } + + _is_readale = true; + _is_writable = false; + } + + protected: + variant get_impl(const variant& obj) const override + { return make_variant(_owner.registry(), _getter(obj.get())); } + + void set_impl(const variant& obj, const variant& value) const override + { } + }; + + template + static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter) + { + return std::make_unique( + owner, + name, + cppmp::make_getter(std::forward(getter))); + } + }; + + /* member_variable_builder - write-only setter */ + + template + struct member_variable_builder< + cppmp::list, + cppmp::enable_if_t< + decltype(cppmp::make_setter)::is_valid_v + && setter_info_trait::is_write_only_v + > + > + { + using setter_type = cppmp::decay_t()))>; + using object_type = typename setter_type::object_type; + using value_type = typename setter_type::value_type; + + struct member_impl + : public member_variable + { + private: + setter_type _setter; + + public: + inline member_impl( + cpprtti::type& p_owner, + const std::string& p_name, + setter_type p_setter) + : member_variable(p_owner, p_name, p_owner.registry().get()) + , _setter(p_setter) + { + if (_owner.id() != get_type_id>()) + { + throw exception( + error_code::class_type_mismatch, + "Object type of the setter does not match the type of the owner!"); + } + + _is_readale = false; + _is_writable = true; + } + + protected: + variant get_impl(const variant& obj) const override + { return variant(); } + + void set_impl(const variant& obj, const variant& value) const override + { _setter(obj.get(), value.get()); } + }; + + template + static constexpr decltype(auto) apply(type& owner, const std::string& name, X_setter&& setter) + { + return std::make_unique( + owner, + name, + cppmp::make_setter(std::forward(setter))); + } + }; + + /* member_variable_builder - read-write getter/setter */ + + template + struct member_variable_builder< + cppmp::list, + cppmp::enable_if_t< + decltype(cppmp::make_getter)::is_valid_v + && decltype(cppmp::make_setter)::is_valid_v + > + > + { + using getter_type = cppmp::decay_t()))>; + using getter_object_type = typename getter_type::object_type; + using getter_value_type = typename getter_type::value_type; + + using setter_type = cppmp::decay_t()))>; + using setter_object_type = typename setter_type::object_type; + using setter_value_type = typename setter_type::value_type; + + using object_type = cppmp::decay_t; + using value_type = cppmp::decay_t; + + static_assert(cppmp::is_same_v< + cppmp::decay_t, + cppmp::decay_t>, + "object type does not match"); + static_assert(cppmp::is_same_v< + cppmp::decay_t, + cppmp::decay_t>, + "value type does not match"); + + struct member_impl + : public member_variable + { + private: + getter_type _getter; + setter_type _setter; + + public: + inline member_impl( + cpprtti::type& p_owner, + const std::string& p_name, + getter_type p_getter, + setter_type p_setter) + : member_variable(p_owner, p_name, p_owner.registry().get()) + , _getter(p_getter) + , _setter(p_setter) + { + if (_owner.id() != get_type_id>()) + { + throw exception( + error_code::class_type_mismatch, + "Object type of the setter does not match the type of the owner!"); + } + + _is_readale = true; + _is_writable = true; + } + + protected: + variant get_impl(const variant& obj) const override + { + return obj.is_const() + ? make_variant(_owner.registry(), _getter(obj.get())) + : make_variant(_owner.registry(), _getter(obj.get())); + } + + void set_impl(const variant& obj, const variant& value) const override + { _setter(obj.get(), value.get()); } + }; + + template + static constexpr decltype(auto) apply(type& owner, const std::string& name, X_getter&& getter, X_setter&& setter) + { + return std::make_unique( + owner, + name, + cppmp::make_getter(std::forward(getter)), + cppmp::make_setter(std::forward(setter))); + } + }; + + } + +} diff --git a/include/cpprtti/types/type.h b/include/cpprtti/types/type.h new file mode 100644 index 0000000..3af7f32 --- /dev/null +++ b/include/cpprtti/types/type.h @@ -0,0 +1,75 @@ +#pragma once + +#include + +#include + +namespace cpprtti +{ + + struct registry; + + enum class rtti_type + { + unknown = 0, //!< unknown + fundamental_type, //!< fundamental C++ type (like int, float, ...) + class_type, //!< C++ class/struct + }; + + using rtti_type_t = enum rtti_type; + + /** + * @brief represents any C++ type + */ + struct type + { + 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 + + public: + /** + * @brief Constructor. + */ + inline type( + cpprtti::registry& p_registry, + size_t p_id, + const std::string& p_name, + rtti_type_t p_rtti_type); + + /** + * @brief Destructor. + */ + virtual ~type() = default; + + /** + * @brief Type registry this type belongs to. + */ + inline cpprtti::registry& registry(); + + /** + * @brief Type registry this type belongs to. + */ + inline const cpprtti::registry& registry() const; + + /** + * @brief Get the ID of this type. + */ + inline type_id id() const; + + /** + * @brief Get the name of this type. + */ + inline const std::string& name() const; + + /** + * @brief Get the RTTI type of this type object. + */ + inline rtti_type_t rtti_type() const; + }; + +} + +#include "type.inl" diff --git a/include/cpprtti/types/type.inl b/include/cpprtti/types/type.inl new file mode 100644 index 0000000..ee2dfa5 --- /dev/null +++ b/include/cpprtti/types/type.inl @@ -0,0 +1,36 @@ +#pragma once + +#include "type.h" + +namespace cpprtti +{ + + /* type */ + + type::type( + cpprtti::registry& p_registry, + size_t p_id, + const std::string& p_name, + rtti_type_t p_rtti_type) + : _registry (p_registry) + , _id (p_id) + , _name (p_name) + , _rtti_type(p_rtti_type) + { } + + cpprtti::registry& type::registry() + { return _registry; } + + const cpprtti::registry& type::registry() const + { return _registry; } + + type_id type::id() const + { return _id; } + + const std::string& type::name() const + { return _name; } + + rtti_type_t type::rtti_type() const + { return _rtti_type; } + +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..d7062c5 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,38 @@ +# Initialize ###################################################################################### + +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 ) + +# Object 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 ) + +# Install ######################################################################################### + +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 ( ) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..fe20cfb --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,52 @@ +# Initialize ###################################################################################### + +Include ( cotire OPTIONAL RESULT_VARIABLE HAS_COTIRE ) +Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) +Include ( cmake_tests OPTIONAL RESULT_VARIABLE HAS_CMAKE_TESTS ) + +# Test ############################################################################################ + +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 ) + +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 ) + + # pedantic + 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 ( ) diff --git a/test/cpprtti/misc/variant_tests.cpp b/test/cpprtti/misc/variant_tests.cpp new file mode 100644 index 0000000..194fd01 --- /dev/null +++ b/test/cpprtti/misc/variant_tests.cpp @@ -0,0 +1,101 @@ +#include +#include + +using namespace cpprtti; + +TEST(variant_test, empty) +{ + variant v; + EXPECT_TRUE (v.empty()); + EXPECT_FALSE(static_cast(v)); +} + +TEST(variant_test, store_normal_value) +{ + registry reg; + int i = 6; + auto v = make_variant(reg, static_cast(i)); + + EXPECT_FALSE (v.is_reference()); + EXPECT_FALSE (v.is_const()); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); +} + +TEST(variant_test, store_constant_value) +{ + registry reg; + int i = 6; + auto v = make_variant(reg, static_cast(i)); + + EXPECT_FALSE (v.is_reference()); + EXPECT_FALSE (v.is_const()); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); +} + +TEST(variant_test, store_lvalue_reference) +{ + registry reg; + int i = 6; + auto v = make_variant(reg, static_cast(i)); + + EXPECT_TRUE (v.is_reference()); + EXPECT_FALSE (v.is_const()); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); +} + +TEST(variant_test, store_constant_lvalue_reference) +{ + registry reg; + int i = 6; + auto v = make_variant(reg, static_cast(i)); + + EXPECT_TRUE (v.is_reference()); + EXPECT_TRUE (v.is_const()); + + EXPECT_EQ (v.get(), 6); + EXPECT_ANY_THROW(v.get()); + EXPECT_ANY_THROW(v.get()); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_ANY_THROW(v.get()); +} + +TEST(variant_test, store_rvalue_reference) +{ + registry reg; + int i = 6; + auto v = make_variant(reg, std::move(i)); + + EXPECT_FALSE (v.is_reference()); + EXPECT_FALSE (v.is_const()); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); + EXPECT_EQ (v.get(), 6); +} diff --git a/test/cpprtti/type/class_type_tests.cpp b/test/cpprtti/type/class_type_tests.cpp new file mode 100644 index 0000000..84f349b --- /dev/null +++ b/test/cpprtti/type/class_type_tests.cpp @@ -0,0 +1,111 @@ +#include +#include + +using namespace cpprtti; + +struct test_class +{ +public: + int i; + std::string read_only; + std::string write_only; + std::string read_write; + +public: + inline const std::string& get_read_only() const + { return read_only; } + + inline void set_write_only(const std::string& value) + { write_only = value; } + + inline const std::string& get_read_write() const + { return read_write; } + + inline void set_read_write(const std::string& value) + { read_write = value; } + + inline int add(int value) + { return i += value; } +}; + +TEST(class_type_tests, member_variable_read_write_getter) +{ + registry reg; + auto& c = reg.get(); + + test_class obj; + obj.i = 10; + + auto& m = c.add_member_variable("i", &test_class::i); + auto v = m.get(obj); + EXPECT_TRUE (m.is_readale()); + EXPECT_TRUE (m.is_writable()); + EXPECT_TRUE (v.is_reference()); + EXPECT_FALSE(v.is_const()); + EXPECT_EQ (v.get(), 10); +} + +TEST(class_type_tests, member_variable_read_only_getter) +{ + registry reg; + auto& c = reg.get(); + + test_class obj; + obj.read_only = "read_only"; + + auto& m = c.add_member_variable("read_only", &test_class::get_read_only); + auto v = m.get(obj); + EXPECT_TRUE (m.is_readale()); + EXPECT_FALSE(m.is_writable()); + EXPECT_TRUE (v.is_reference()); + EXPECT_TRUE (v.is_const()); + EXPECT_EQ (v.get(), "read_only"); +} + +TEST(class_type_tests, member_variable_write_only_setter) +{ + registry reg; + auto& c = reg.get(); + + test_class obj; + + auto& m = c.add_member_variable("write_only", &test_class::set_write_only); + EXPECT_FALSE(m.is_readale()); + EXPECT_TRUE (m.is_writable()); + m.set(obj, std::string("fuuu")); + EXPECT_EQ(obj.write_only, "fuuu"); +} + +TEST(class_type_tests, member_variable_read_write_getter_setter) +{ + registry reg; + auto& c = reg.get(); + + test_class obj; + obj.read_write = "read_write"; + + auto& m = c.add_member_variable("read_write", &test_class::get_read_write, &test_class::set_read_write); + auto v = m.get(obj); + EXPECT_TRUE (m.is_readale()); + EXPECT_TRUE (m.is_writable()); + EXPECT_TRUE (v.is_reference()); + EXPECT_TRUE (v.is_const()); + EXPECT_EQ (v.get(), "read_write"); + m.set(obj, std::string("fuuu")); + EXPECT_EQ(obj.read_write, "fuuu"); +} + +TEST(class_type_tests, member_method) +{ + registry reg; + auto& c = reg.get(); + + test_class obj; + obj.i = 10; + + auto& m = c.add_member_method("add", &test_class::add); + auto v = m.invoke(obj, 5); + + EXPECT_EQ(v.get(), 15); + EXPECT_EQ(obj.i, 15); +}