From 62367f83b983accd9b9eef6fd1db69e589feaa16 Mon Sep 17 00:00:00 2001 From: Axel Menzel <info@axelmenzel.de> Date: Tue, 27 Jan 2015 19:46:35 +0100 Subject: [PATCH] initial commit --- CMake/config.cmake | 111 + CMake/installDependencies.cmake | 42 + CMake/installer.cmake | 67 + CMake/utility.cmake | 393 + CMakeLists.txt | 69 + LICENSE | 26 + README | 56 + doc/CMakeLists.txt | 84 + doc/DoxyFile.cfg | 2369 +++++ doc/License.dox | 76 + doc/MainPage.dox | 109 + doc/Tutorial.dox | 920 ++ doc/index.html | 6 + src/CMakeLists.txt | 35 + src/benchmark/CMakeLists.txt | 55 + src/benchmark/benchmark_rttr_cast.cpp | 842 ++ src/benchmark/benchmark_utils.h | 94 + src/benchmark/main.cpp | 331 + src/benchmark/pch.h | 20 + src/benchmark/test_classes.h | 169 + src/rttr/CMakeLists.txt | 140 + src/rttr/base/core_prerequisites.h | 185 + src/rttr/base/version.h.in | 37 + src/rttr/constructor.cpp | 222 + src/rttr/constructor.h | 274 + src/rttr/destructor.cpp | 92 + src/rttr/destructor.h | 127 + src/rttr/detail/accessor_type.h | 124 + src/rttr/detail/argument.h | 104 + src/rttr/detail/array_accessor.h | 708 ++ src/rttr/detail/array_container.h | 731 ++ src/rttr/detail/array_container_base.h | 95 + src/rttr/detail/array_mapper.h | 248 + src/rttr/detail/constructor_container.h | 141 + .../detail/constructor_container_base.cpp | 147 + src/rttr/detail/constructor_container_base.h | 85 + src/rttr/detail/destructor_container.h | 59 + src/rttr/detail/destructor_container_base.cpp | 44 + src/rttr/detail/destructor_container_base.h | 61 + src/rttr/detail/enumeration_container.h | 117 + .../detail/enumeration_container_base.cpp | 57 + src/rttr/detail/enumeration_container_base.h | 81 + src/rttr/detail/function_traits.h | 166 + src/rttr/detail/instance.h | 116 + src/rttr/detail/metadata_container.cpp | 79 + src/rttr/detail/metadata_container.h | 67 + src/rttr/detail/method_accessor.h | 329 + src/rttr/detail/method_container.h | 106 + src/rttr/detail/method_container_base.cpp | 99 + src/rttr/detail/method_container_base.h | 91 + src/rttr/detail/misc_type_traits.h | 396 + src/rttr/detail/property_accessor.h | 79 + src/rttr/detail/property_container.h | 60 + src/rttr/detail/property_container_base.cpp | 66 + src/rttr/detail/property_container_base.h | 89 + src/rttr/detail/property_container_func.h | 202 + .../detail/property_container_member_func.h | 220 + .../detail/property_container_member_object.h | 192 + src/rttr/detail/property_container_object.h | 178 + src/rttr/detail/reflection_database.cpp | 204 + src/rttr/detail/reflection_database_p.h | 140 + src/rttr/detail/standard_types.cpp | 72 + src/rttr/detail/standard_types.h | 121 + src/rttr/detail/standard_types_char.cpp | 90 + src/rttr/detail/std_conversion_functions.cpp | 319 + src/rttr/detail/std_conversion_functions.h | 94 + src/rttr/detail/type_converter.h | 77 + src/rttr/detail/utility.h | 193 + src/rttr/enumeration.cpp | 167 + src/rttr/enumeration.h | 222 + src/rttr/impl/register_reflection_impl.h | 637 ++ src/rttr/impl/rttr_cast_impl.h | 52 + src/rttr/impl/type_impl.h | 349 + src/rttr/impl/variant_array_impl.h | 140 + src/rttr/impl/variant_default_types_impl.h | 263 + src/rttr/impl/variant_impl.h | 672 ++ src/rttr/metadata.h | 66 + src/rttr/method.cpp | 238 + src/rttr/method.h | 296 + src/rttr/pch.h | 16 + src/rttr/policy.cpp | 43 + src/rttr/policy.h | 160 + src/rttr/property.cpp | 197 + src/rttr/property.h | 271 + src/rttr/reflect | 45 + src/rttr/register_reflection.h | 725 ++ src/rttr/rttr.cmake | 103 + src/rttr/rttr_cast.h | 50 + src/rttr/rttr_enable.h | 297 + src/rttr/type | 47 + src/rttr/type.cpp | 857 ++ src/rttr/type.h | 730 ++ src/rttr/variant.cpp | 1038 ++ src/rttr/variant.h | 567 ++ src/rttr/variant_array.cpp | 363 + src/rttr/variant_array.h | 513 + src/rttr/version.rc.in | 82 + src/test/CMakeLists.txt | 66 + src/test/catch.hpp | 8423 +++++++++++++++++ src/test/main.cpp | 29 + src/test/pch.h | 20 + src/test/test_classes.h | 189 + src/test/test_constructor_reflection.cpp | 180 + src/test/test_constructor_reflection.h | 43 + src/test/test_enumeration_reflection.cpp | 184 + src/test/test_enumeration_reflection.h | 72 + src/test/test_method_reflection.cpp | 397 + src/test/test_method_reflection.h | 106 + src/test/test_property_reflection.cpp | 627 ++ src/test/test_property_reflection.h | 126 + src/test/test_type.cpp | 355 + src/test/test_type.h | 26 + src/test/test_variant.cpp | 788 ++ src/test/test_variant.h | 0 114 files changed, 34435 insertions(+) create mode 100644 CMake/config.cmake create mode 100644 CMake/installDependencies.cmake create mode 100644 CMake/installer.cmake create mode 100644 CMake/utility.cmake create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 README create mode 100644 doc/CMakeLists.txt create mode 100644 doc/DoxyFile.cfg create mode 100644 doc/License.dox create mode 100644 doc/MainPage.dox create mode 100644 doc/Tutorial.dox create mode 100644 doc/index.html create mode 100644 src/CMakeLists.txt create mode 100644 src/benchmark/CMakeLists.txt create mode 100644 src/benchmark/benchmark_rttr_cast.cpp create mode 100644 src/benchmark/benchmark_utils.h create mode 100644 src/benchmark/main.cpp create mode 100644 src/benchmark/pch.h create mode 100644 src/benchmark/test_classes.h create mode 100644 src/rttr/CMakeLists.txt create mode 100644 src/rttr/base/core_prerequisites.h create mode 100644 src/rttr/base/version.h.in create mode 100644 src/rttr/constructor.cpp create mode 100644 src/rttr/constructor.h create mode 100644 src/rttr/destructor.cpp create mode 100644 src/rttr/destructor.h create mode 100644 src/rttr/detail/accessor_type.h create mode 100644 src/rttr/detail/argument.h create mode 100644 src/rttr/detail/array_accessor.h create mode 100644 src/rttr/detail/array_container.h create mode 100644 src/rttr/detail/array_container_base.h create mode 100644 src/rttr/detail/array_mapper.h create mode 100644 src/rttr/detail/constructor_container.h create mode 100644 src/rttr/detail/constructor_container_base.cpp create mode 100644 src/rttr/detail/constructor_container_base.h create mode 100644 src/rttr/detail/destructor_container.h create mode 100644 src/rttr/detail/destructor_container_base.cpp create mode 100644 src/rttr/detail/destructor_container_base.h create mode 100644 src/rttr/detail/enumeration_container.h create mode 100644 src/rttr/detail/enumeration_container_base.cpp create mode 100644 src/rttr/detail/enumeration_container_base.h create mode 100644 src/rttr/detail/function_traits.h create mode 100644 src/rttr/detail/instance.h create mode 100644 src/rttr/detail/metadata_container.cpp create mode 100644 src/rttr/detail/metadata_container.h create mode 100644 src/rttr/detail/method_accessor.h create mode 100644 src/rttr/detail/method_container.h create mode 100644 src/rttr/detail/method_container_base.cpp create mode 100644 src/rttr/detail/method_container_base.h create mode 100644 src/rttr/detail/misc_type_traits.h create mode 100644 src/rttr/detail/property_accessor.h create mode 100644 src/rttr/detail/property_container.h create mode 100644 src/rttr/detail/property_container_base.cpp create mode 100644 src/rttr/detail/property_container_base.h create mode 100644 src/rttr/detail/property_container_func.h create mode 100644 src/rttr/detail/property_container_member_func.h create mode 100644 src/rttr/detail/property_container_member_object.h create mode 100644 src/rttr/detail/property_container_object.h create mode 100644 src/rttr/detail/reflection_database.cpp create mode 100644 src/rttr/detail/reflection_database_p.h create mode 100644 src/rttr/detail/standard_types.cpp create mode 100644 src/rttr/detail/standard_types.h create mode 100644 src/rttr/detail/standard_types_char.cpp create mode 100644 src/rttr/detail/std_conversion_functions.cpp create mode 100644 src/rttr/detail/std_conversion_functions.h create mode 100644 src/rttr/detail/type_converter.h create mode 100644 src/rttr/detail/utility.h create mode 100644 src/rttr/enumeration.cpp create mode 100644 src/rttr/enumeration.h create mode 100644 src/rttr/impl/register_reflection_impl.h create mode 100644 src/rttr/impl/rttr_cast_impl.h create mode 100644 src/rttr/impl/type_impl.h create mode 100644 src/rttr/impl/variant_array_impl.h create mode 100644 src/rttr/impl/variant_default_types_impl.h create mode 100644 src/rttr/impl/variant_impl.h create mode 100644 src/rttr/metadata.h create mode 100644 src/rttr/method.cpp create mode 100644 src/rttr/method.h create mode 100644 src/rttr/pch.h create mode 100644 src/rttr/policy.cpp create mode 100644 src/rttr/policy.h create mode 100644 src/rttr/property.cpp create mode 100644 src/rttr/property.h create mode 100644 src/rttr/reflect create mode 100644 src/rttr/register_reflection.h create mode 100644 src/rttr/rttr.cmake create mode 100644 src/rttr/rttr_cast.h create mode 100644 src/rttr/rttr_enable.h create mode 100644 src/rttr/type create mode 100644 src/rttr/type.cpp create mode 100644 src/rttr/type.h create mode 100644 src/rttr/variant.cpp create mode 100644 src/rttr/variant.h create mode 100644 src/rttr/variant_array.cpp create mode 100644 src/rttr/variant_array.h create mode 100644 src/rttr/version.rc.in create mode 100644 src/test/CMakeLists.txt create mode 100644 src/test/catch.hpp create mode 100644 src/test/main.cpp create mode 100644 src/test/pch.h create mode 100644 src/test/test_classes.h create mode 100644 src/test/test_constructor_reflection.cpp create mode 100644 src/test/test_constructor_reflection.h create mode 100644 src/test/test_enumeration_reflection.cpp create mode 100644 src/test/test_enumeration_reflection.h create mode 100644 src/test/test_method_reflection.cpp create mode 100644 src/test/test_method_reflection.h create mode 100644 src/test/test_property_reflection.cpp create mode 100644 src/test/test_property_reflection.h create mode 100644 src/test/test_type.cpp create mode 100644 src/test/test_type.h create mode 100644 src/test/test_variant.cpp create mode 100644 src/test/test_variant.h diff --git a/CMake/config.cmake b/CMake/config.cmake new file mode 100644 index 00000000..5f7894b9 --- /dev/null +++ b/CMake/config.cmake @@ -0,0 +1,111 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +# setup version numbers +set(RTTR_VERSION_MAJOR 0) +set(RTTR_VERSION_MINOR 9) +set(RTTR_VERSION_PATCH 0) +set(RTTR_VERSION ${RTTR_VERSION_MAJOR}.${RTTR_VERSION_MINOR}.${RTTR_VERSION_PATCH}) +set(RTTR_VERSION_STR "${RTTR_VERSION_MAJOR}.${RTTR_VERSION_MINOR}.${RTTR_VERSION_PATCH}") +math(EXPR RTTR_VERSION_CALC "${RTTR_VERSION_MAJOR}*1000 + ${RTTR_VERSION_MINOR}*100 + ${RTTR_VERSION_PATCH}") +set(RTTR_PRODUCT_NAME "RTTR") +message("Project version: ${RTTR_VERSION_STR}") + +# files +set(README_FILE "${CMAKE_SOURCE_DIR}/README") +set(LICENSE_FILE "${CMAKE_SOURCE_DIR}/LICENSE") + +# dirs where the binaries should be placed, isntalled +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +set(CMAKE_EXECUTABLE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +set(RTTR_INSTALL_DIR "${CMAKE_BINARY_DIR}/install") + +getNameOfDir(CMAKE_LIBRARY_OUTPUT_DIRECTORY RTTR_TARGET_BIN_DIR) +is_vs_based_build(VS_BUILD) +if (VS_BUILD) + #set(RTTR_BIN_INSTALL_DIR ${RTTR_TARGET_BIN_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}) + set(RTTR_BIN_INSTALL_DIR ${RTTR_TARGET_BIN_DIR}) + set(RTTR_LIB_INSTALL_DIR "lib") +else() + set(RTTR_BIN_INSTALL_DIR ${RTTR_TARGET_BIN_DIR}) + set(RTTR_LIB_INSTALL_DIR "lib") +endif() + +set(CMAKE_DEBUG_POSTFIX "_d") + +# detect architecture +include(CheckTypeSize) +check_type_size(void* SIZEOF_VOID_PTR) +if(${SIZEOF_VOID_PTR} MATCHES "^8$") + set(RTTR_NATIVE_ARCH 64) +else() + set(RTTR_NATIVE_ARCH 32) +endif() + +enable_rtti(BUILD_WITH_RTTI) + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + message(STATUS "added flag -std=c++0x to g++") + else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + message(STATUS "added flag -std=c++11 to g++") + endif() + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "4.0.0") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden") + endif() +endif() + +# RelWithDepInfo should have the same option like the Release build +# but of course with Debug informations +if(MSVC) + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Zi /DEBUG") +elseif(CMAKE_COMPILER_IS_GNUCXX ) + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g") +else() + message(WARNING "Please adjust CMAKE_CXX_FLAGS_RELWITHDEBINFO flags for this compiler!") +endif() + +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "${CMAKE_CURRENT_BINARY_DIR}/CMake/rttr-config-version.cmake" + VERSION ${RTTR_VERSION_STR} + COMPATIBILITY AnyNewerVersion +) + +install( + FILES + "${CMAKE_CURRENT_BINARY_DIR}/CMake/rttr-config-version.cmake" + DESTINATION + cmake + COMPONENT + Devel +) \ No newline at end of file diff --git a/CMake/installDependencies.cmake b/CMake/installDependencies.cmake new file mode 100644 index 00000000..c832b4b4 --- /dev/null +++ b/CMake/installDependencies.cmake @@ -0,0 +1,42 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +#################################################################################### +# Install dependencies +# +#################################################################################### + +MESSAGE(STATUS ${LIBRARY_OUTPUT_DIRECTORY}) +MESSAGE(STATUS "Copy Dependencies...") +MESSAGE(STATUS "===========================") + +install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE" "${CMAKE_CURRENT_SOURCE_DIR}/README" + DESTINATION "${RTTR_INSTALL_DIR}" + PERMISSIONS OWNER_READ) + + +MESSAGE(STATUS "Finished copying dependencies!") \ No newline at end of file diff --git a/CMake/installer.cmake b/CMake/installer.cmake new file mode 100644 index 00000000..f0a38d00 --- /dev/null +++ b/CMake/installer.cmake @@ -0,0 +1,67 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +# first step set up all necessary variables +if(WIN32 AND NOT UNIX) + set(CPACK_GENERATOR ZIP) +elseif(UNIX AND NOT APPLE) + set(CPACK_GENERATOR TGZ;TBZ2) +elseif(APPLE) + set(CPACK_GENERATOR TGZ;TBZ2;PackageMaker) +endif() + +set(CPACK_PACKAGE_NAME "${RTTR_PRODUCT_NAME}") +set(CPACK_PACKAGE_VENDOR "AMS") +set(CPACK_PACKAGE_VERSION_MAJOR "${RTTR_VERSION_MAJOR}") +set(CPACK_PACKAGE_VERSION_MINOR "${RTTR_VERSION_MINOR}") +set(CPACK_PACKAGE_VERSION_PATCH "${RTTR_VERSION_PATCH}") +set(CPACK_PACKAGE_VERSION "${RTTR_VERSION}") +set(CPACK_RESOURCE_FILE_README "${README_FILE}") +set(CPACK_PACKAGE_DESCRIPTION_FILE "${README_FILE}") +set(CPACK_RESOURCE_FILE_LICENSE "${LICENSE_FILE}") +set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_VENDOR}/RTTR") + +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "RTTR is a C++ runtime reflection library") + +# detect system +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(RTTR_OS_NAME "linux${RTTR_NATIVE_ARCH}") +elseif(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + set(RTTR_OS_NAME "win${RTTR_NATIVE_ARCH}") +else() + message(WARNING "This system is not supported for packing") + return() +endif() + +getCompilerName(RTTR_COMPILER_NAME) + +# we use the same naming scheme like Qt +set(CPACK_PACKAGE_FILE_NAME "rttr-${RTTR_VERSION_STR}-${RTTR_OS_NAME}-${RTTR_COMPILER_NAME}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "rttr-${RTTR_VERSION_STR}-src") + +# now as last step we can include CPack +include(CPack) \ No newline at end of file diff --git a/CMake/utility.cmake b/CMake/utility.cmake new file mode 100644 index 00000000..e0f50cd9 --- /dev/null +++ b/CMake/utility.cmake @@ -0,0 +1,393 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +#################################################################################### +# Welcome to the CMake build system for RTTR. +# This file contains several helper function to make the life easier with cmake. +#################################################################################### + +#################################################################################### +# create hierarchical source groups, useful for big VS-Projects +# FILE_LIST <= a list of files with absolut path +#################################################################################### +function (createSrcGroups FILE_LIST ) + # we want to get the relative path from the + # current source dir + string(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} curDirLen) + set(TMP_FILE_LIST ${${FILE_LIST}}) + + foreach ( SOURCE ${TMP_FILE_LIST} ) + string(LENGTH ${SOURCE} fullPathLen) + math(EXPR RelPathLen ${fullPathLen}-${curDirLen}) + string(SUBSTRING ${SOURCE} ${curDirLen} ${RelPathLen} curStr) + + string ( REGEX REPLACE "[\\/]" "\\\\" normPath ${curStr} ) + string ( REGEX MATCH "\\\\(.*)\\\\" ouput ${normPath} ) + if(NOT "${CMAKE_MATCH_1}" STREQUAL "") + source_group ( ${CMAKE_MATCH_1} FILES ${SOURCE} ) + endif() + endforeach() +endfunction() + + #################################################################################### + # HEADER_FILES <= the possible header files which contain a Q_OBJECT (input) + # MOC_SOURCES => the generated moc source files (output) + #################################################################################### + macro (GenerateMocFiles HEADER_FILES MOC_SOURCES) + set (MOC_HEADERS) + foreach(CAND ${HEADER_FILES}) + file(STRINGS ${CAND} var REGEX "Q_OBJECT") + if(var) + list(APPEND MOC_HEADERS ${CAND}) + endif() + endforeach() + # MOC_SOURCES => outputfiles + # MOC_HEADERS => inputfiless + QT4_WRAP_CPP(${MOC_SOURCES} ${MOC_HEADERS}) + source_group("Generated Files" FILES ${${MOC_SOURCES}}) +endmacro () + +#################################################################################### + + macro (GenerateUIFiles HEADER_FILES UI_HEADERS) + QT4_WRAP_UI(${UI_HEADERS} ${HEADER_FILES}) + source_group("Generated Files" FILES ${${UI_HEADERS}}) +endmacro() + +#################################################################################### + +macro (GenerateRCFiles RESSOURCE_FILES RC_FILES) + QT4_ADD_RESOURCES(RC_FILES ${RESSOURCE_FILES}) + source_group("Ressource Files" FILES ${${RC_FILES}}) +endmacro() + +#################################################################################### +# Create a UnityFile. This is a file which inlcudes all other source files. +# This is usefull, when you want a fast rebuild. +# _UNITY_FILE <= The name of the UnityFile +# _SRC_FILES <= The list of source files +#################################################################################### +function(generateUnityFile _UNITY_FILE _SRC_FILES) + set(files ${${_SRC_FILES}}) + # Generate a unique filename for the unity build translation unit + set(unit_build_file ${CMAKE_CURRENT_BINARY_DIR}/ub_${_UNITY_FILE}.cpp) + set_source_files_properties(${files} PROPERTIES HEADER_FILE_ONLY true) + # Open the ub file + FILE(WRITE ${unit_build_file} "// Unity Build generated by CMake\n") + # Add include statement for each translation unit + foreach(sourceFile ${files} ) + #FILE( APPEND ${unit_build_file} "#include <${CMAKE_CURRENT_SOURCE_DIR}/${sourceFile}>\n") + FILE( APPEND ${unit_build_file} "#include \"${sourceFile}\"\n") + endforeach() + # Complement list of translation units with the name of ub + set(${_UNITY_FILE} ${unit_build_file} PARENT_SCOPE) + source_group("Generated Files" FILES ${unit_build_file}) +endfunction() + +#################################################################################### +# Returns the name of the Directory, where the file in the FILE_PATH is located. +#################################################################################### +function(getNameOfDir FILE_PATH DIR_NAME) + get_filename_component(HAS_FILE_IN_PATH ${${FILE_PATH}} EXT) + if (HAS_FILE_IN_PATH) + get_filename_component(PATH_WITHOUT_FILENAME ${${FILE_PATH}} PATH) + get_filename_component(NAME_OF_DIR ${PATH_WITHOUT_FILENAME} NAME) + set(${DIR_NAME} ${NAME_OF_DIR} PARENT_SCOPE) + else() + get_filename_component(NAME_OF_DIR ${${FILE_PATH}} NAME) + set(${DIR_NAME} ${NAME_OF_DIR} PARENT_SCOPE) + endif() +endfunction() + +#################################################################################### +# Returns relative path from the given file path; starting from CMAKE_CURRENT_SOURCE_DIR +#################################################################################### + +function(getRelativePath FILE_PATH RELATIVE_PATH) + string(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} CUR_DIR_LEN) + get_filename_component(PATH_WITHOUT_FILE ${${FILE_PATH}} PATH) + string(LENGTH ${PATH_WITHOUT_FILE} FULL_PATH_LEN) + math(EXPR REL_PATH_LEN ${FULL_PATH_LEN}-${CUR_DIR_LEN}) + math(EXPR REL_PATH_START "${CUR_DIR_LEN}") + string(SUBSTRING ${PATH_WITHOUT_FILE} ${REL_PATH_START} ${REL_PATH_LEN} REL_PATH) + string(REGEX REPLACE "^/" "" out_path "${REL_PATH}") + set(${RELATIVE_PATH} ${out_path} PARENT_SCOPE) +endfunction() + +#################################################################################### +# Loads a FOLDER, which should contain a FOLDER.cmake. +# In this file all source and header files should be declared. +# In this cmake files all files have to be declared relative. +# They will be read with absolut path. +# FOLDER <= The name of the folder +# _HEADER_FILES => The list of header files +# _SOURCE_FILES => The list of source files +#################################################################################### +function(loadFolder FOLDER _HEADER_FILES _SOURCE_FILES) + set(FULL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${FOLDER}.cmake) + include(${FULL_PATH}) + get_filename_component(ABS_PATH_TO_FILES ${FULL_PATH} PATH) + foreach(headerFile ${HEADER_FILES} ) + if (${headerFile} MATCHES ".*.h.in$") + string ( REGEX REPLACE ".h.in$" ".h" out_path ${headerFile} ) + configure_file(${headerFile} ${CMAKE_CURRENT_BINARY_DIR}/${out_path} @ONLY) + set(FULL_HEADER_PATH ${ABS_PATH_TO_FILES}/${headerFile}) + getRelativePath(FULL_HEADER_PATH REL_PATH) + set(FULL_HEADER_PATH ${CMAKE_CURRENT_BINARY_DIR}/${out_path}) + source_group ( ${REL_PATH} FILES ${FULL_HEADER_PATH} ) + list(APPEND ALL_HPP_FILES ${FULL_HEADER_PATH}) + else() + set(FULL_HEADER_PATH ${ABS_PATH_TO_FILES}/${headerFile}) + # returns the relative path, from the current source dir + getRelativePath(FULL_HEADER_PATH REL_PATH) + list(APPEND HEADER_LIST_OF_CUR_DIR ${FULL_HEADER_PATH}) + endif() + # get the name of the current directory + getNameOfDir(CMAKE_CURRENT_SOURCE_DIR DIRNAME) + # we don't want to install header files which are marked as private + if (NOT ${FULL_HEADER_PATH} MATCHES ".*_p.h$") + install(FILES ${FULL_HEADER_PATH} DESTINATION "include/${DIRNAME}/${REL_PATH}" PERMISSIONS OWNER_READ) + endif() + endforeach() + # and now the source files + foreach(srcFile ${SOURCE_FILES} ) + list(APPEND SOURCE_LIST_OF_CUR_DIR ${ABS_PATH_TO_FILES}/${srcFile}) + endforeach() + + list(APPEND ALL_HPP_FILES ${${_HEADER_FILES}} ${HEADER_LIST_OF_CUR_DIR}) + list(APPEND ALL_CPP_FILES ${${_SOURCE_FILES}} ${SOURCE_LIST_OF_CUR_DIR}) + set(${_HEADER_FILES} ${ALL_HPP_FILES} PARENT_SCOPE) + set(${_SOURCE_FILES} ${ALL_CPP_FILES} PARENT_SCOPE) + + createSrcGroups(HEADER_LIST_OF_CUR_DIR) + createSrcGroups(SOURCE_LIST_OF_CUR_DIR) + message( STATUS "${FOLDER} directory included" ) +endfunction() + +#################################################################################### +# This function checks if the current generator is used for a Viusal Studio build +# _INPUT This variable will be set to TRUE if its a Visual Studio build, otherwise to FALSE. +#################################################################################### +function (is_vs_based_build _INPUT) + if(${CMAKE_GENERATOR} MATCHES "Visual Studio") + set(${_INPUT} TRUE PARENT_SCOPE) + else() + set(${_INPUT} FALSE PARENT_SCOPE) + endif() +endfunction() + +#################################################################################### +# Copy a release dependency in the correct CMAKE_RUNTIME_OUTPUT_DIRECTORY +# _INPUT The full path of the dependency incl. FileName +# _OUTPUT The directory where the libraries should be installed. +#################################################################################### + +function(copy_dependency_release _INPUT _OUTPUT) + is_vs_based_build(VS_BUILD) + + # when this is a DEBUG build we dont copy the files + if(NOT VS_BUILD) + if(${CMAKE_BUILD_TYPE} STREQUAL Debug) + return() + endif() + endif() + + set(_PATH ${_INPUT}) + # make the path to normal path with / as dir separator + string ( REGEX REPLACE "[\\/]" "////" FILE_PATH ${_PATH} ) + get_filename_component(FILE_NAME ${FILE_PATH} NAME) + + if (VS_BUILD) + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Release/${FILE_NAME} COPYONLY) + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/RelWithDebInfo/${FILE_NAME} COPYONLY) + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MinSizeRel/${FILE_NAME} COPYONLY) + else() + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FILE_NAME} COPYONLY) + endif() + + install(FILES + ${FILE_PATH} + DESTINATION ${_OUTPUT} + CONFIGURATIONS Release) +endfunction() + +#################################################################################### +# Copy a debug dependency in the correct CMAKE_RUNTIME_OUTPUT_DIRECTORY +# _INPUT The full path of the dependency incl. FileName +# _OUTPUT The directory where the libraries should be installed. +#################################################################################### + +function(copy_dependency_debug _INPUT _OUTPUT) + is_vs_based_build(VS_BUILD) + + # when this is NOT a DEBUG build we dont copy the files + if(NOT VS_BUILD) + if(NOT ${CMAKE_BUILD_TYPE} STREQUAL Debug) + return() + endif() + endif() + + set(_PATH ${_INPUT}) + # make the path to normal path with / as dir separator + string ( REGEX REPLACE "[\\/]" "////" FILE_PATH ${_PATH} ) + get_filename_component(FILE_NAME ${FILE_PATH} NAME) + + if (VS_BUILD) + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/Debug/${FILE_NAME} COPYONLY) + else() + configure_file(${FILE_PATH} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${FILE_NAME} COPYONLY) + endif() + + install(FILES + ${FILE_PATH} + DESTINATION ${_OUTPUT} + CONFIGURATIONS Debug) +endfunction() + +#################################################################################### +# Creates a PreCompiled Header +# _PRECOMPILED_HEADER The full path of the dependency incl. FileName +# _SRC_FILES (Only works with CPP files) +#################################################################################### +function(activate_precompiled_headers _PRECOMPILED_HEADER _SOURCE_FILES) + set(SRC_FILES ${${_SOURCE_FILES}}) + get_filename_component(pch_basename ${_PRECOMPILED_HEADER} NAME_WE) + set(pch_abs ${CMAKE_CURRENT_SOURCE_DIR}/${_PRECOMPILED_HEADER}) + set(pch_unity ${CMAKE_CURRENT_BINARY_DIR}/${pch_basename}.cpp) + + if(MSVC) + # First specify the name of the PCH file + # it seems to be that nmake build cant handle the $(IntDir) variable + if(${CMAKE_GENERATOR} MATCHES "NMake") + set(pch_bin ${CMAKE_CURRENT_BINARY_DIR}/${pch_basename}.pch) + else() + set(pch_bin "$(IntDir)/${pch_basename}.pch") + endif() + # Generate precompiled header translation unit + if (NOT EXISTS ${pch_unity}) + file(WRITE ${pch_unity} "// Precompiled header unity generated by CMake\n") + file(APPEND ${pch_unity} "#include <${pch_abs}>\n") + endif() + # this creates the precompild header + set_source_files_properties(${pch_unity} + PROPERTIES COMPILE_FLAGS "/Yc\"${pch_abs}\" /Fp\"${pch_bin}\"" + OBJECT_OUTPUTS "${pch_bin}") + # Update properties of source files to use the precompiled header. + # Additionally, force the inclusion of the precompiled header at beginning of each source file. + set_source_files_properties(${SRC_FILES} + PROPERTIES COMPILE_FLAGS "/Yu\"${pch_abs}\" /FI\"${pch_abs}\" /Fp\"${pch_bin}\"" + OBJECT_DEPENDS "${pch_bin}") + # Finally, update the source file collection to contain the precompiled header translation unit + set(${_SOURCE_FILES} ${pch_unity} ${${_SOURCE_FILES}} PARENT_SCOPE) + source_group("Generated Files" FILES ${pch_unity}) + endif() +endfunction() + +#################################################################################### +# Adds or replace a compiler option +# _OLD_OPTION The option which should be replaced +# _NEW_OPTION The new option which should be added +#################################################################################### +macro( replaceCompilerOption _OLD_OPTION _NEW_OPTION) + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES ${_OLD_OPTION}) + string(REGEX REPLACE ${_OLD_OPTION} ${_NEW_OPTION} ${flag_var} "${${flag_var}}") + else() + set(${flag_var} "${${flag_var}} ${_NEW_OPTION}") + endif() + # set(${flag_var} ${${flag_var}} PARENT_SCOPE) + endforeach() +endmacro() + +#################################################################################### +# enables or disables the user of RTTI for all following source files. +# _ENABLE If true, will enable RTTI, otherwise will disable RTTI. +#################################################################################### +macro(enable_rtti _ENABLE) + set(enable_rtti_opt "") + set(disable_rtti_opt "") + if (MSVC) + set(enable_rtti_opt "/GR") + set(disable_rtti_opt "/GR-") + elseif(CMAKE_COMPILER_IS_GNUCXX ) + set(disable_rtti_opt "-fno-rtti") + endif() + + if (${_ENABLE}) + message(STATUS "Enabled: use of RTTI") + replaceCompilerOption("${disable_rtti_opt}" "${enable_rtti_opt}") + else() + message(STATUS "Disabled: use of RTTI") + replaceCompilerOption(${enable_rtti_opt} ${disable_rtti_opt}) + endif() +endmacro() + + +#################################################################################### +# Returns the name of the used compiler. +# _COMPILER_NAME +#################################################################################### +function(getCompilerName _COMPILER_NAME) + if(MSVC_VERSION EQUAL 1400) + set(COMPILER_NAME "vs2005") + elseif(MSVC_VERSION EQUAL 1500) + set(COMPILER_NAME "vs2008") + elseif(MSVC_VERSION EQUAL 1600) + set(COMPILER_NAME "vs2010") + elseif(MSVC_VERSION EQUAL 1700) + set(COMPILER_NAME "vs2012") + elseif(MSVC_VERSION EQUAL 1800) + set(COMPILER_NAME "vs2013") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set(COMPILER_NAME "gcc") + if(WIN32) + execute_process(COMMAND "${CMAKE_CXX_COMPILER}" "-dumpversion" OUTPUT_VARIABLE GCC_VERSION_OUTPUT) + string(REGEX REPLACE "([0-9]+\\.[0-9]+).*" "\\1" GCC_VERSION "${GCC_VERSION_OUTPUT}") + set(COMPILER_NAME ${COMPILER_NAME}${GCC_VERSION}) + endif() + else() + message(WARNING "Can not retrieve compiler name!") + return() + endif() + + set(${_COMPILER_NAME} ${COMPILER_NAME} PARENT_SCOPE) +endfunction() + + +#################################################################################### +# Get environment variable, define it as ENV_$var and make sure backslashes are converted to forward slashes +# _COMPILER_NAME +#################################################################################### +macro(getenv_path VAR) + set(ENV_${VAR} $ENV{${VAR}}) + # replace won't work if var is blank + if (ENV_${VAR}) + string( REGEX REPLACE "\\\\" "/" ENV_${VAR} ${ENV_${VAR}} ) + endif () +endmacro() diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..9266803b --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,69 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +#################################################################################### +# Welcome to the CMake build system for RTTR( Run Time Type Reflection). # +# This is the main file where the general build environment is set-up and the # +# the build configuration options are initialized. # +#################################################################################### + +cmake_minimum_required (VERSION 2.8.12) + +project ("rttr") + +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/CMake" + ) + +if (CMAKE_BUILD_TYPE STREQUAL "") + # CMake defaults to leaving CMAKE_BUILD_TYPE empty. This screws up + # differentiation between debug and release builds. + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: None (CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE) +endif () + +# our little cmake helper functions +include(utility) + +# set up option variable for cmake +option(BUILD_STATIC "Build RTTR as static library" FALSE) +option(BUILD_WITH_STATIC_RUNTIME_LIBS "Link against the static runtime libraries" FALSE) +option(BUILD_WITH_RTTI "Enable build with C++ runtime type information for compilation" TRUE) +option(CREATE_UNITY_BUILD "Creates a unity build" FALSE) +option(USE_PCH "Use precompiled header files for compilation" TRUE) + +include(config) +include(installDependencies) +include(installer) + +# here we add our source code +add_subdirectory(src) +# and the documentation +add_subdirectory(doc) + +# here we specify the installation directory +set(CMAKE_INSTALL_PREFIX + ${RTTR_INSTALL_DIR} CACHE PATH "RTTR install prefix" FORCE) diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..2bc548a8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ \ No newline at end of file diff --git a/README b/README new file mode 100644 index 00000000..9a702219 --- /dev/null +++ b/README @@ -0,0 +1,56 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +Build instruction for RTTR with Windows and Visual Studio 2012 and above: +================================================================ +1. Set environment variables in command shell: +set CMAKE_ROOT = <Your path to cmake directory> - REQUIRED + +set DOXYGEN_ROOT = <Your path to cmake directory> - OPTIONAL otherwise no documentation will be generated + +Start CMake: +e.g. +cmake -G "Visual Studio 11" <Absolute Path to RTTR source code directory> +or with make files: +cmake -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles" <Absolute Path to RTTR source code directory> +// compile with +nmake install + + + +Build instruction for RTTR with Linux and g++: +================================================================ +1. Set environment variables in your shell: +CMAKE_ROOT => <Your path to cmake directory> - REQUIRED + +DOXYGEN_ROOT => <Your path to cmake directory> - OPTIONAL otherwise no documentation generated + +Start CMake: +e.g. +cmake -DCMAKE_BUILD_TYPE=Release <Absolute Path to RTTR source code directory> +// compile with: +make install diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 00000000..f98cf8ac --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,84 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +find_package(Doxygen) +# These variables are defined in the doxygen file(@VAR@) as place holders +# an will be substituted by the following values +set(DOXYGEN_PROJECT_NUMBER "${RTTR_VERSION_STR}") +set(DOXYGEN_PROJECT_NAME "${PROJECT_NAME}") +set(DOXYGEN_INPUT_DIRECTORY "${CMAKE_SOURCE_DIR}") +set(DOXYGEN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/doc") +set(DOXYGEN_DOC_INSTALL_DIR "doc") +set(DOXYGEN_CONFIG_FILE "DoxyFile.cfg") +set(DOXYGEN_EXCLUDE "") +set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_SOURCE_DIR}/src/rttr") +set(DOXYGEN_PREDEFINED "RTTR_EXPORT= DOXYGEN") +set(DOXYGEN_EXCLUDE_SYMBOLS "impl* \\ + impl::* \\ + detail* \\ + detail:: \\ + *MetaTypeInfo* \\ + *method_container* \\ + *property_container* \\ + ") + +set(DOXYGEN_EXCLUDE_PATTERNS "*.cpp* \\ + *_p.cpp* \\ + *PCH.h* \\ + *_impl.h* \\ + *_p.h* \\ + */src/test/* \\ + */src/benchmark/*") + +if(NOT DOXYGEN_FOUND) + message(WARNING "Doxygen not found - Documentation will not be generated!") + return() +endif() + +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${DOXYGEN_CONFIG_FILE}) + message(WARNING "Doxygen configuration file not found - Documentation will not be generated!") + return() +endif() + +# modify the doxygen file, i.e. substiture the variables with values +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${DOXYGEN_CONFIG_FILE} ${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_CONFIG_FILE}) +# and also the redirect short cut +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/index.html DESTINATION ${DOXYGEN_OUTPUT_DIRECTORY}) + +add_custom_target(Doc ALL + COMMAND "${DOXYGEN_EXECUTABLE}" "${DOXYGEN_OUTPUT_DIRECTORY}/${DOXYGEN_CONFIG_FILE}" + WORKING_DIRECTORY "${DOXYGEN_OUTPUT_DIRECTORY}" + COMMENT "Building documentation...") + +install(DIRECTORY "${DOXYGEN_OUTPUT_DIRECTORY}/html" + DESTINATION "${DOXYGEN_DOC_INSTALL_DIR}" + PATTERN "*.*" + PERMISSIONS OWNER_READ) + +install(FILES "${DOXYGEN_OUTPUT_DIRECTORY}/index.html" + DESTINATION "${DOXYGEN_DOC_INSTALL_DIR}" + PERMISSIONS OWNER_READ) diff --git a/doc/DoxyFile.cfg b/doc/DoxyFile.cfg new file mode 100644 index 00000000..c4891f7f --- /dev/null +++ b/doc/DoxyFile.cfg @@ -0,0 +1,2369 @@ +# Doxyfile 1.8.7 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = @DOXYGEN_PROJECT_NAME@ + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is included in +# the documentation. The maximum height of the logo should not exceed 55 pixels +# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo +# to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = @DOXYGEN_STRIP_FROM_PATH@ + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a +# new page for each member. If set to NO, the documentation of a member will be +# part of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO these classes will be included in the various overviews. This option has +# no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = YES + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = YES + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the +# todo list. This list is created by putting \todo commands in the +# documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the +# test list. This list is created by putting \test commands in the +# documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if <section_label> ... \endif and \cond <section_label> +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES the list +# will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. Do not use file names with spaces, bibtex cannot handle them. See +# also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = NO + +# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = NO + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO doxygen will only warn about wrong or incomplete parameter +# documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOXYGEN_INPUT_DIRECTORY@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank the +# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, +# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, +# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, +# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, +# *.qsf, *.as and *.js. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.d \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.idl \ + *.odl \ + *.cs \ + *.php \ + *.php3 \ + *.inc \ + *.m \ + *.mm \ + *.dox \ + *.py \ + *.f90 \ + *.f \ + *.vhd \ + *.vhdl + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = @DOXYGEN_EXCLUDE_PATTERNS@ + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = @DOXYGEN_EXCLUDE_SYMBOLS@ + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# <filter> <input-file> +# +# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER ) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES, then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES, then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# compiled with the --with-libclang option. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 7 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- +# defined cascading style sheet that is included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefor more robust against future updates. +# Doxygen will copy the style sheet file to the output directory. For an example +# see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the stylesheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to NO can help when comparing the output of multiple runs. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler ( hhc.exe). If non-empty +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated ( +# YES) or that it should be included in the master .chm file ( NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated ( +# YES) or a normal table of contents ( NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using prerendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use <access key> + S +# (what the <access key> is depends on the OS and browser, but it is typically +# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down +# key> to jump into the search results window, the results can be navigated +# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel +# the search. The filter options can be selected when the cursor is inside the +# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys> +# to select a filter and <Enter> or <escape> to activate or cancel the filter +# option. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. There +# are two flavors of web server based searching depending on the EXTERNAL_SEARCH +# setting. When disabled, doxygen will generate a PHP script for searching and +# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing +# and searching needs to be provided by external tools. See the section +# "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain the +# search results. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). +# +# See the section "External Indexing and Searching" for details. +# The default value is: NO. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will return the search results when EXTERNAL_SEARCH is enabled. +# +# Doxygen ships with an example indexer ( doxyindexer) and search engine +# (doxysearch.cgi) which are based on the open source search engine library +# Xapian (see: http://xapian.org/). See the section "External Indexing and +# Searching" for details. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. +# The default file is: searchdata.xml. +# This tag requires that the tag SEARCHENGINE is set to YES. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of +# to a relative location where the documentation can be found. The format is: +# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... +# This tag requires that the tag SEARCHENGINE is set to YES. + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# Configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. +# The default value is: YES. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. +# +# Note that when enabling USE_PDFLATEX this option is only used for generating +# bitmaps for formulas in the HTML output, but not in the Makefile that is +# written to the output directory. +# The default file is: latex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate +# index for LaTeX. +# The default file is: makeindex. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES doxygen generates more compact LaTeX +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used by the +# printer. +# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x +# 14 inches) and executive (7.25 x 10.5 inches). +# The default value is: a4. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names +# that should be included in the LaTeX output. To get the times font for +# instance you can specify +# EXTRA_PACKAGES=times +# If left blank no extra packages will be included. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the +# generated LaTeX document. The header should contain everything until the first +# chapter. If it is left blank doxygen will generate a standard header. See +# section "Doxygen usage" for information on how to let doxygen write the +# default header to a separate file. +# +# Note: Only use a user-defined header if you know what you are doing! The +# following commands have a special meaning inside the header: $title, +# $datetime, $date, $doxygenversion, $projectname, $projectnumber. Doxygen will +# replace them by respectively the title of the page, the current date and time, +# only the current date, the version number of doxygen, the project name (see +# PROJECT_NAME), or the project number (see PROJECT_NUMBER). +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the +# generated LaTeX document. The footer should contain everything after the last +# chapter. If it is left blank doxygen will generate a standard footer. +# +# Note: Only use a user-defined footer if you know what you are doing! +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the LATEX_OUTPUT output +# directory. Note that the files will be copied as-is; there are no commands or +# markers available. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is +# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will +# contain links (just like the HTML output) instead of page references. This +# makes the output suitable for online browsing using a PDF viewer. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +PDF_HYPERLINKS = YES + +# If the LATEX_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate +# the PDF file directly from the LaTeX files. Set this option to YES to get a +# higher quality PDF documentation. +# The default value is: YES. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode +# command to the generated LaTeX files. This will instruct LaTeX to keep running +# if errors occur, instead of asking the user for help. This option is also used +# when generating formulas in HTML. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BATCHMODE = NO + +# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the +# index chapters (such as File Index, Compound Index, etc.) in the output. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_HIDE_INDICES = NO + +# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source +# code with syntax highlighting in the LaTeX output. +# +# Note that which sources are shown also depends on other settings such as +# SOURCE_BROWSER. +# The default value is: NO. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. See +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# The default value is: plain. +# This tag requires that the tag GENERATE_LATEX is set to YES. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# Configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES doxygen will generate RTF output. The +# RTF output is optimized for Word 97 and may not look too pretty with other RTF +# readers/editors. +# The default value is: NO. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: rtf. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES doxygen generates more compact RTF +# documents. This may be useful for small projects and may help to save some +# trees in general. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will +# contain hyperlink fields. The RTF file will contain links (just like the HTML +# output) instead of page references. This makes the output suitable for online +# browsing using Word or some other Word compatible readers that support those +# fields. +# +# Note: WordPad (write) and others do not support links. +# The default value is: NO. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's config +# file, i.e. a series of assignments. You only have to provide replacements, +# missing definitions are set to their default value. +# +# See also section "Doxygen usage" for information on how to generate the +# default style sheet that doxygen normally uses. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an RTF document. Syntax is +# similar to doxygen's config file. A template extensions file can be generated +# using doxygen -e rtf extensionFile. +# This tag requires that the tag GENERATE_RTF is set to YES. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES doxygen will generate man pages for +# classes and files. +# The default value is: NO. + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. A directory man3 will be created inside the directory specified by +# MAN_OUTPUT. +# The default directory is: man. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to the generated +# man pages. In case the manual section does not start with a number, the number +# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is +# optional. +# The default value is: .3. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_EXTENSION = .3 + +# The MAN_SUBDIR tag determines the name of the directory created within +# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by +# MAN_EXTENSION with the initial . removed. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_SUBDIR = + +# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it +# will generate one additional man file for each entity documented in the real +# man page(s). These additional files only source the real man page, but without +# them the man command would be unable to find the correct page. +# The default value is: NO. +# This tag requires that the tag GENERATE_MAN is set to YES. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES doxygen will generate an XML file that +# captures the structure of the code including all documentation. +# The default value is: NO. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: xml. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES doxygen will dump the program +# listings (including syntax highlighting and cross-referencing information) to +# the XML output. Note that enabling this will significantly increase the size +# of the XML output. +# The default value is: YES. +# This tag requires that the tag GENERATE_XML is set to YES. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# Configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES doxygen will generate Docbook files +# that can be used to generate PDF. +# The default value is: NO. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. +# The default directory is: docbook. +# This tag requires that the tag GENERATE_DOCBOOK is set to YES. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# Configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES doxygen will generate an AutoGen +# Definitions (see http://autogen.sf.net) file that captures the structure of +# the code including all documentation. Note that this feature is still +# experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES doxygen will generate a Perl module +# file that captures the structure of the code including all documentation. +# +# Note that this feature is still experimental and incomplete at the moment. +# The default value is: NO. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES doxygen will generate the necessary +# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI +# output from the Perl module output. +# The default value is: NO. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be nicely +# formatted so it can be parsed by a human reader. This is useful if you want to +# understand what is going on. On the other hand, if this tag is set to NO the +# size of the Perl module output will be much smaller and Perl will parse it +# just the same. +# The default value is: YES. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file are +# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful +# so different doxyrules.make files included by the same Makefile don't +# overwrite each other's variables. +# This tag requires that the tag GENERATE_PERLMOD is set to YES. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES doxygen will evaluate all +# C-preprocessor directives found in the sources and include files. +# The default value is: YES. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES doxygen will expand all macro names +# in the source code. If set to NO only conditional compilation will be +# performed. Macro expansion can be done in a controlled way by setting +# EXPAND_ONLY_PREDEF to YES. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then +# the macro expansion is limited to the macros specified with the PREDEFINED and +# EXPAND_AS_DEFINED tags. +# The default value is: NO. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES the includes files in the +# INCLUDE_PATH will be searched if a #include is found. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by the +# preprocessor. +# This tag requires that the tag SEARCH_INCLUDES is set to YES. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will be +# used. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that are +# defined before the preprocessor is started (similar to the -D option of e.g. +# gcc). The argument of the tag is a list of macros of the form: name or +# name=definition (no spaces). If the definition and the "=" are omitted, "=1" +# is assumed. To prevent a macro definition from being undefined via #undef or +# recursively expanded use the := operator instead of the = operator. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +PREDEFINED = @DOXYGEN_PREDEFINED@ + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this +# tag can be used to specify a list of macro names that should be expanded. The +# macro definition that is found in the sources will be used. Use the PREDEFINED +# tag if you want to use a different macro definition that overrules the +# definition found in the source code. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will +# remove all references to function-like macros that are alone on a line, have +# an all uppercase name, and do not end with a semicolon. Such function macros +# are typically used for boiler-plate code, and will confuse the parser if not +# removed. +# The default value is: YES. +# This tag requires that the tag ENABLE_PREPROCESSING is set to YES. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration options related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tag files. For each tag +# file the location of the external documentation should be added. The format of +# a tag file without this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where loc1 and loc2 can be relative or absolute paths or URLs. See the +# section "Linking to external documentation" for more information about the use +# of tag files. +# Note: Each tag file must have a unique name (where the name does NOT include +# the path). If a tag file is not located in the directory in which doxygen is +# run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create a +# tag file that is based on the input files it reads. See section "Linking to +# external documentation" for more information about the usage of tag files. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external class will be listed in the +# class index. If set to NO only the inherited external classes will be listed. +# The default value is: NO. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed in +# the modules index. If set to NO, only the current project's groups will be +# listed. +# The default value is: YES. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed in +# the related pages index. If set to NO, only the current project's pages will +# be listed. +# The default value is: YES. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of 'which perl'). +# The default file (with absolute path) is: /usr/bin/perl. + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES doxygen will generate a class diagram +# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to +# NO turns the diagrams off. Note that this option also works with HAVE_DOT +# disabled, but it is recommended to install and use dot, since it yields more +# powerful graphs. +# The default value is: YES. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see: +# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# You can include diagrams made with dia in doxygen documentation. Doxygen will +# then run dia to produce the diagram and insert it in the documentation. The +# DIA_PATH tag allows you to specify the directory where the dia binary resides. +# If left empty dia is assumed to be found in the default search path. + +DIA_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide inheritance +# and usage relations if the target is undocumented or is not a class. +# The default value is: YES. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz (see: +# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent +# Bell Labs. The other options in this section have no effect if this option is +# set to NO +# The default value is: NO. + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed +# to run in parallel. When set to 0 doxygen will base this on the number of +# processors available in the system. You can set it explicitly to a value +# larger than 0 to get control over the balance between CPU load and processing +# speed. +# Minimum value: 0, maximum value: 32, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_NUM_THREADS = 1 + +# When you want a differently looking font n the dot files that doxygen +# generates you can specify the font name using DOT_FONTNAME. You need to make +# sure dot is able to find the font, which can be done by putting it in a +# standard location or by setting the DOTFONTPATH environment variable or by +# setting DOT_FONTPATH to the directory containing the font. +# The default value is: Helvetica. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTNAME = + +# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of +# dot graphs. +# Minimum value: 4, maximum value: 24, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the default font as specified with +# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set +# the path where dot can find it using this tag. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_FONTPATH = + +# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for +# each documented class showing the direct and indirect inheritance relations. +# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a +# graph for each documented class showing the direct and indirect implementation +# dependencies (inheritance, containment, and class references variables) of the +# class with other documented classes. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for +# groups, showing the direct groups dependencies. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside the +# class node. If there are many fields or methods and many nodes the graph may +# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the +# number of items for each type to make the size more manageable. Set this to 0 +# for no limit. Note that the threshold may be exceeded by 50% before the limit +# is enforced. So when you set the threshold to 10, up to 15 fields may appear, +# but if the number exceeds 15, the total amount of fields shown is limited to +# 10. +# Minimum value: 0, maximum value: 100, default value: 10. +# This tag requires that the tag HAVE_DOT is set to YES. + +UML_LIMIT_NUM_FIELDS = 10 + +# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and +# collaboration graphs will show the relations between templates and their +# instances. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +TEMPLATE_RELATIONS = NO + +# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to +# YES then doxygen will generate a graph for each documented file showing the +# direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDE_GRAPH = YES + +# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are +# set to YES then doxygen will generate a graph for each documented file showing +# the direct and indirect include dependencies of the file with other documented +# files. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH tag is set to YES then doxygen will generate a call +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller +# dependency graph for every global function or class method. +# +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical +# hierarchy of all classes instead of a textual one. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the +# dependencies a directory has on other directories in a graphical way. The +# dependency relations are determined by the #include relations between the +# files in the directories. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. +# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order +# to make the SVG files visible in IE 9+ (other browsers do not have this +# requirement). +# Possible values are: png, jpg, gif and svg. +# The default value is: png. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# +# Note that this requires a modern browser other than Internet Explorer. Tested +# and working are Firefox, Chrome, Safari, and Opera. +# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make +# the SVG files visible. Older versions of IE do not have SVG support. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +INTERACTIVE_SVG = NO + +# The DOT_PATH tag can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the \dotfile +# command). +# This tag requires that the tag HAVE_DOT is set to YES. + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the \mscfile +# command). + +MSCFILE_DIRS = + +# The DIAFILE_DIRS tag can be used to specify one or more directories that +# contain dia files that are included in the documentation (see the \diafile +# command). + +DIAFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes +# that will be shown in the graph. If the number of nodes in a graph becomes +# larger than this value, doxygen will truncate the graph, which is visualized +# by representing a node as a red box. Note that doxygen if the number of direct +# children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that +# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. +# Minimum value: 0, maximum value: 10000, default value: 50. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs +# generated by dot. A depth value of 3 means that only nodes reachable from the +# root by following a path via at most 3 edges will be shown. Nodes that lay +# further from the root node will be omitted. Note that setting this option to 1 +# or 2 may greatly reduce the computation time needed for large code bases. Also +# note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. +# Minimum value: 0, maximum value: 1000, default value: 0. +# This tag requires that the tag HAVE_DOT is set to YES. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not seem +# to support this out of the box. +# +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) support +# this, this feature is disabled by default. +# The default value is: NO. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page +# explaining the meaning of the various boxes and arrows in the dot generated +# graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES doxygen will remove the intermediate dot +# files that are used to generate the various graphs. +# The default value is: YES. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_CLEANUP = YES diff --git a/doc/License.dox b/doc/License.dox new file mode 100644 index 00000000..5ac4bb4a --- /dev/null +++ b/doc/License.dox @@ -0,0 +1,76 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +/* +@file License.h +@author Axel Menzel +@brief Contains the license page for the documentation of the RTTR Project +*/ + +// This file contains only comments for doxygen + +/*! +\page page_license License +\tableofcontents +The rttr library can be used under the following license (<a href="http://opensource.org/licenses/mit-license.php">MIT</a>) license. +\verbatim +RTTR (Run Time Type Reflection) is made available under the MIT License. + +Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +\endverbatim +When you use the RTTR library in your software the only condition is, that you distribute this license text. + +\section used_libs Third party libaries +<a href="https://github.com/philsquared/Catch">catch</a> - but only for unit tests, +License: <a href="https://github.com/philsquared/Catch/blob/master/LICENSE_1_0.txt">Boost Software License</a> + +\section author Author + Axel Menzel (info@axelmenzel.de) + +<hr> +\par Go To Section... +- \ref page_tutorial +- \ref page_license +*/ diff --git a/doc/MainPage.dox b/doc/MainPage.dox new file mode 100644 index 00000000..bde5986a --- /dev/null +++ b/doc/MainPage.dox @@ -0,0 +1,109 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +/* +@file Mainpage.h +@author Axel Menzel +@brief Contains development documentation for the RTTR Project +*/ + +// This file contains only comments for doxygen + +/*! +\mainpage RTTR - Run Time Type Reflection +\tableofcontents +\section intro Introduction +The _run time type reflection_ (RTTR) library adds the missing feature of reflection to C++. <br> +That means the programmer can introspect an object at runtime of what kind of properties, methods and constructors it consist.<br> +This is extremely useful when a tight but also high dynamic coupling between software modules is required.<br> +The main use cases are for example serialisation, UI creation, binding to arbitrary programming languages (JavaScript, Lua etc.) and network communication. + +It works without any extra preprocess pass to reflect your type, only standard C++11 language features are used.<br> +However, in order to reflect your types it is required to manually register your classes with its properties and methods. <br> +While registering methods it is usually not necessary to know the exact signature, since the library <br> +will generate a wrapper class for this depending on the compile-time type of the method.<br> +In order to avoid header pollution, the registration process should be done completely in the cpp file. There is no need to derive from some super base class. + +RTTR is released under the terms of the \ref page_license "MIT license". + +\section features Features +RTTR supports: +- a faster and across shared libraries working replacement of rtti +- classes; with single-, multiple- and virtual-inheritance (requires one macro placement inside class) +- constructors (arbitrary argument count) +- properties +- methods (virtual, abstract, overloaded, arbitrary argument count) +- enums (C++11 enum class) +- arrays (incl. raw-arrays; arbitrary dimension count) +- ability to invoke properties and methods of classes from any arbitrary class level +- no header pollution; the reflection information is created in the cpp file to minimize compile time when modifying the data +- working with custom types without the need of having the declaration of the type available at compile time (useful for plugins) +- possibility to add additional metadata to all reflection objects +- adjust binding behaviour through return value policies +- minimal macro usage +- no exceptions (this feature come with <a target="_blank" href=http://preshing.com/20110807/the-cost-of-enabling-exception-handling/>cost</a> and is also regularly disabled on consoles) +- no external compiler or tool needed, only standard ISO C++11 + +\section portability Portability +RTTR is compiled and tested on following platforms: +- Microsoft Visual C++ 12 (2013) x32 and x64 +- gcc 4.8.1 x32 and x64 + +<hr> + +\section content Content +- \ref page_tutorial + - \ref declare_types + - \ref declare_simple_types + - \ref declare_class_hierarchies + - \ref declare_types_use_type + - \ref declare_rttr_cast + - \ref binding_types + - \ref binding_types_hello_world + - \ref binding_types_methods + - \ref binding_types_properties + - \ref binding_types_enums + - \ref binding_types_variant + - \ref binding_types_classes + - \ref binding_types_meta_data + - \ref binding_types_policies +- \ref page_license + +<hr> + +\section acknowledgements Acknowledgements +The following persons has been very helpful: +- Ville Voutilainen, for reviewing and improving the code base +- Paul Mensonides, for given me an introduction in preprocessor metaprogramming + + +*/ + +/*! + * @namespace rttr + * @brief The namespace for all rttr components + */ diff --git a/doc/Tutorial.dox b/doc/Tutorial.dox new file mode 100644 index 00000000..81da7ec6 --- /dev/null +++ b/doc/Tutorial.dox @@ -0,0 +1,920 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +/* +@file Tutorial.dox +@author Axel Menzel +@brief Contains the tutorial for RTTR +*/ + +// This file contains only comments for doxygen + +namespace rttr +{ + +/*! +\page page_tutorial Tutorial +\tableofcontents +Before retrieving data from RTTR you have register your custom `Type` and the following tutorial will show how to do this. +The register process has to be done manually and can be separated in two steps: + + 1. \ref declare_types "declaring" your `Type` + 2. \ref binding_types "binding" the properties, methods, enums or constructors of your `Type` + +\section declare_types Declaring Types + +RTTR uses an own, in ISO C++ implemented, alternative to the build in RTTI mechanism of C++. The reason for that are problems when using typeid <a target="_blank" href=https://svn.boost.org/trac/boost/ticket/754>across shared boundaries</a> +and the <a target="_blank" href=http://tinodidriksen.com/2010/04/14/cpp-dynamic-cast-performance/>execution speed</a>. +In order to use this mechanism you have to register them with a macro named \ref RTTR_DECLARE_TYPE. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection declare_simple_types 1. Simple Types +Suppose we have a struct named `MyStruct` +\code{.cpp} +// MyStruct.h +struct MyStruct +{ + int i; +}; +RTTR_DECLARE_TYPE(MyStruct) +\endcode + +Place the macro inside the header of the struct or class, just below the declaration. +The macro will create a register function and is also responsible for returning the corresponding \ref type object for this type.<br> +The registration process itself is now done at runtime. + +When `MyStruct` is in a namespace, make sure you put the macro outside the namespace, otherwise the \ref type class cannot access the `Type`. +\code{.cpp} + namespace MyNameSpace + { + //.... + } + RTTR_DECLARE_TYPE(MyNameSpace::MyStruct) +\endcode + +Also note, that when your are working with pointers of your custom type, then these needs to be registered too. +\code{.cpp} + RTTR_DECLARE_TYPE(MyStruct) // raw type + RTTR_DECLARE_TYPE(MyStruct*) // ptr type + RTTR_DECLARE_TYPE(const MyStruct*) // ptr to const type +\endcode + +Instead of writing tree times \ref RTTR_DECLARE_TYPE you can use the shortcut macro \ref RTTR_DECLARE_STANDARD_TYPE_VARIANTS + +\code{.cpp} + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(MyStruct) +\endcode + +This will declare for a given type `Type`, the following types: `Type`, `Type*`, `const Type*` + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection declare_class_hierarchies 2. Class Hierarchies +When you use class hierarchies, you have to put a certain macro inside every class. +Otherwise you are not able to retrieve the information about the most derived type of your current instance. +The macro you have to insert is called \ref RTTR_ENABLE + +Suppose we have a base struct called `Base`. +\code{.cpp} +// Base.h +struct Base +{ + RTTR_ENABLE() +}; +RTTR_DECLARE_TYPE(Base) +\endcode +Place the macro \ref RTTR_ENABLE() somewhere in the class, it doesn't matter if its under the public, protected or private class accessor section. + +Into the derived class you put the same macro, but now as argument the name of the parent class. +Which is in this case `Base`. +\code{.cpp} +// Derived.h +struct Derived : Base +{ + RTTR_ENABLE(Base) +}; +RTTR_DECLARE_TYPE(Derived) // don't forget to declare your type +\endcode + +When you use multiple inheritance you simply separate every class with a comma. +\code{.cpp} +// MultipleDerived.h +struct MultipleDerived : Base, Other +{ + RTTR_ENABLE(Base, Other) +}; +RTTR_DECLARE_TYPE(MultipleDerived) +\endcode +Remark that the order in which you declare here the multiple inheritance, has an impact later when retrieving properties of a class. +So it is best practice to use the same order like in your class. +RTTR supports to register even virtual base classes. + +\remark The only limitation you have is: It is **not** possible to register a class twice in the same class hierarchy. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection declare_types_use_type 3. type as alternative to typeid +When you have successfully registered your custom type, you can use \ref type to check whether the current instance is a certain type or not.<br> +There are three static template member functions for retrieving the \ref type: + +type::get(const char*) + +This function just expects the name of the type. This is useful when you know only the name of the type and cannot include the type itself into the source code. +The name of the type is the same like you have registered with \ref RTTR_DECLARE_TYPE but as string literal. When you have used a typedef then you need to provide this typedef also as string literal. + +\code{.cpp} + type::get("int") == type::get("int"); // yields to true + type::get("bool") == type::get("int"); // yields to false + type::get("MyNameSpace::MyStruct") == type::get("MyNameSpace::MyStruct"); // yields to true +\endcode + +type::get<T>() + +This function just expects one template argument. Use it to check against a known type. + +\code{.cpp} + type::get<int>() == type::get<int>(); // yields to true + type::get<int>() == type::get<bool>(); // yields to false +\endcode + +type::get<T>(T&& obj) + +This function is a universal reference and returns from every given object the corresponding type object. +\code{.cpp} + int int_obj; + int* int_obj_ptr = &int_obj; + const int* c_int_obj_ptr = int_obj_ptr; + type::get<int>() == type::get(int_obj); // yields to true + type::get<int*>() == type::get(int_obj_ptr); // yields to true + type::get<const int*>() == type::get(c_int_obj_ptr);// yields to true +\endcode + +When this function is called for a glvalue expression whose type is a polymorphic class type, +then the result refers to a \ref type object representing the type of the most derived object. + +\code{.cpp} + struct Base {}; + struct Derived : Base {}; + Derived d; + Base& base = d; + type::get<Derived>() == type::get(base) // yields to true + type::get<Base>() == type::get(base) // yields to false + + // remark, when called with pointers: + Base* base_ptr = &d; + type::get<Derived>() == type::get(base_ptr); // yields to false + type::get<Base*>() == type::get(base_ptr); // yields to true +\endcode + +\remark If the type of the expression is a cv-qualified type, the result of the rttr::type::get expression refers to a rttr::type object representing the cv-unqualified type. + +\code{.cpp} + class D { ... }; + D d1; + const D d2; + type::get(d1) == type::get(d2); // yields true + type::get<D>() == type::get<const D>(); // yields true + type::get<D>() == type::get(d2); // yields true + type::get<D>() == type::get<const D&>(); // yields true + type::get<D>() == type::get<const D*>(); // yields false +\endcode + +Any `top level` cv-qualifier of the given type `T` will be removed. + + <!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection declare_rttr_cast 4. rttr_cast as alternative to dynamic_cast +Providing an own function for `dynamic_cast` completes the package as an `RTTI` alternative.<br> +The function rttr_cast<T>(Arg) allows the client to cast between class hierarchies up and down, +cross casts between unrelated classes and even class hierarchies with virtual inheritance. +The target type `T` can be also in the middle of the hierarchy. + \code{.cpp} + struct A { ... }; + struct B : A { ... }; + struct C : B { ... }; + C c; + A* a = &c; + B* b = rttr_cast<B*>(a); // successful + \endcode + An rttr::type object knows from which parent class it is derived. Assumed this information is given via \ref RTTR_ENABLE. +From the functionality it is similar to `dynamic_cast`. +\remark Because exception are not supported the target type `T` can only be a pointer type. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\section binding_types Binding Types +\subsection binding_types_hello_world 1. Hello World + +Let's start with the traditional hello world example. + +\code{.cpp} +#include <rttr/reflect> + +static void f() { std::cout << "Hello World" << std::endl; } +using namespace rttr; + +RTTR_REGISTER +{ + method_("f", &f); +} + +int main() +{ + type::invoke("f", {}); +} +// outputs: "Hello World" +\endcode + +When you need to reflect a property, or like in this case a free function, +you need to include first `#include <rttr/reflect>`. This will include everything necessary for creating +the reflection information. The macro \ref RTTR_REGISTER must be placed outside of any function or class, just place directly into in your cpp file. +This macro executes the register process before *main* is called, +that has the advantage that this reflection information is directly available when main is called. +Remark that this macro can only be placed one-time in a source file, otherwise you will get an compile error. + +The shortest way to invoke the function `f()` is to call \ref type::invoke(const std::string&, std::vector< detail::argument >) "type::invoke()". +It requires the exact name of the free function and a vector of arguments. +You can check whether the call was successful with checking the return value. When the returned \ref variant is valid the call was successful, otherwise not. + +In RTTR is the most important class the \ref type class. With that class you get access to everything else. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_methods 2. Methods +As already mentioned to bind global methods to RTTR use the function \ref rttr::method_(). +It has following synopsis: +\code{.cpp} + template<typename F> + void rttr::method_( const std::string & name, F function, std::vector< rttr::metadata > data); +\endcode +- name is the name of the function +- F is the function pointer or std::function your want to register +- data contains metadata for this function; this is an optional parameter + +For example when you want to register the byte string to integer conversion function: `int atoi (const char * str);` + +Do it in the following way: +\code{.cpp} +RTTR_REGISTER +{ + method_("atoi", &atoi); +} +\endcode + +\subsubsection binding_types_method_overloading 2.1 Overloaded Methods +When you want to register a function which is overloaded (same name, different signature), you have to explicitly provide the signature. +Otherwise C++ can not deduce which function you refer to. +For example when you have two function `float sin (float x);` and `double sin (double x);` : + +\code{.cpp} +RTTR_REGISTER +{ + method_("sin", static_cast<float(*)(float)>(&sin)); + method_("sin", static_cast<double(*)(double)>(&sin)); +} +\endcode + +That is the general syntax for function pointers: +\code{.cpp} +return-value (*func-name)(arg1-type, arg2-type, ...) +\endcode + +\subsubsection binding_types_method_invoke 2.2 Invoke of methods +Invoking a method with RTTR can be done in two ways. +- calling \ref type::invoke(const std::string&, std::vector< detail::argument >) "type::invoke()" from the \ref type class. +- retrieving first a \ref method object from \ref type::get_global_method(const std::string &, const std::vector< type >&) "type::get_global_method()" and then calling invoke + +The first option needs less typing, but it is slower when you need to invoke the function several times. +For the second option you need more code to write, but it invokes the method faster. + +The following example demonstrates the possibilities to invoke a method: +\code{.cpp} +int main() +{ + // let asume we have registered the pow function: double pow( double base, double exp ); + // option 1 + variant return_value = type::invoke("pow", {9.0, 2.0}); + if (return_value.is_valid() && return_value.is_type<double>()) + std::cout << return_value.get_value<double>() << std::endl; // 81 + + // option 2 + method meth = type::get_global_method("pow"); + if (meth) // check if the function was found + { + return_value = meth.invoke(empty_instance(), 9.0, 3.0); + if (return_value.is_valid() && return_value.is_type<double>()) + std::cout << return_value.get_value<double>() << std::endl; // 729 + + } +} +\endcode + +The \ref type::invoke(const std::string&, std::vector< detail::argument >) "type::invoke()" function will internally try +to find a function based on the given name and the given types of the arguments. So finding the correct function when overloaded function are registered is automatically done. +Notice that you have to provide the arguments as a vector pack. Therefore an initializer list is quite handy to reduce typing. +The argument must match 100%, there is at the moment no conversion done. That means, when an integer argument is needed and you forward a double +value the function will **not** be called. The arguments will not be copied, instead they will be wrapped in an internal class and forwarded to the +underlying function pointer. This class is called `detail::argument` do **not** create this class on your own. The class will implicit wrap your argument value. + +The return value of \ref type::invoke(const std::string&, std::vector< detail::argument >) "type::invoke()" is a \ref variant object. +It indicates whether the function was called or not. RTTR does **not** use the exception mechanism of C++, therefore you have to check the return values when you are interested +in a successfull call. The \ref variant object \ref variant::is_valid "is valid" when it was successfully called. When the function has a return value, then this value is also contained +in the variant object. + +When you prefer to hold a handle to the function use the getter \ref type::get_global_method(const std::string &, const std::vector< type >&) "type::get_global_method()" +to retrieve a \ref method object. This has the advantage that you do not need to search for the function every time you want to invoke it. +Additionally this class provides \ref method::invoke "functions" to invoke a function without the need to create a vector of arguments. +The method object is very lightweight and can be simply copied around different locations. The object stays valid till end of the `main()` function. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_properties 3. Properties +For binding a property to RTTR you can use following functions: \ref rttr::property_() and \ref rttr::property_readonly_(). + +They have following synopsis: +\code{.cpp} + template<typename A> + void rttr::property_( const std::string & name, A accessor, std::vector< rttr::metadata > data ); + + template<typename A> + void rttr::property_readonly_( const std::string & name, A accessor, std::vector< rttr::metadata > data ); +\endcode +- name is the name of the property +- A is the pointer to the property +- data contains metadata for this property; this is an optional parameter + +It is also possible to use function pointers for the property as getter and setter functions. +Therefore the \ref rttr::property_( const std::string &, A1, A2 , std::vector< rttr::metadata >) "rttr::property_()" function is overloaded. + +It has following synposis: +\code{.cpp} + template<typename A1, typename A2> + void rttr::property_( const std::string & name, A1 getter, A2 setter, std::vector< rttr::metadata > data ); +\endcode +- name is the name of the property +- A1 is the function pointer to the getter and A2 is the function pointer to the setter of the property +- data contains metadata for this property; this is an optional parameter + +The following example shows how to use these register functions: +\code{.cpp} +static const double pi = 3.14259; +static std::string global_text; +void set_text(const std::string& text) { global_text = text; } +const std::string& get_text() { return global_text; } + +RTTR_REGISTER +{ + property_readonly_("PI", &pi); + property_("global_text", &get_text, &set_text); +} +\endcode + +There can be not two global properties with the same name. The later registered property with an already existing name will be discarded. + +\subsubsection binding_types_property_invoke 3.1 Invoke properties +For setting and getting a property with RTTR you have two options like with methods: +- calling \ref type::set_property_value(const std::string&, detail::argument) "type::set_property_value()" and \ref type::get_property_value(const std::string&) "type::get_property_value()" from the \ref type class or +- retrieving a \ref property object from \ref type::get_global_property(const std::string &) "type::get_global_property()" and then calling \ref property::set_value() and \ref property::get_value + +\code{.cpp} +int main() +{ + // option 1, via type + variant value = type::get_property_value("pi"); + if (value && value.is_type<double>()) + std::cout << value.get_value<double>() << std::endl; // outputs: "3.14259" + + // option 2, via property class + property prop = type::get_property_value("pi"); + if (prop) + { + value = prop.get_value(); + if (value.is_valid() && value.is_type<double>()) + std::cout << value.get_value<double>() << std::endl; // outputs: "3.14259" + } +} +\endcode +The static \ref type::set_property_value(const std::string&, detail::argument) "type::set_property_value()" function calls directly a global property with the given name. +This function has a bool value as return value, indicating whether the property was invoked or not. For retrieving a property value use +the static function \ref type::get_property_value(const std::string&) "type::get_property_value()". The returned \ref variant object contains +the property value and also indicates whether the call to retrieve the property was successful or not. +When the variant is \ref variant::is_valid() "not valid" then the call could not be done. + +Another option is to retrieve a handle to the property via \ref type::get_global_property(const std::string &) "type::get_global_property()". +This is the preferred option, because then you directly set/get the value without searching every time for the property. +The property object is very lightweight and can be simply copied around different locations. The object stays valid till end of the `main()` function. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_enums 4. Enums +RTTR allows also to bind enumerated constants (enums). Therefore use the function \ref enumeration_(). + +It has following synposis: +\code{.cpp} + template<typename EnumType> + void rttr::enumeration_( std::vector< std::pair< std::string, EnumType > > enum_data, std::vector< rttr::metadata > data ); +\endcode + +- enum_data contains a list of *key* to *value* pairs +- data contains metadata for this property; this is an optional parameter + +\code{.cpp} +enum E_Alignment +{ + AlignLeft = 0x0001, + AlignRight = 0x0002, + AlignHCenter = 0x0004, + AlignJustify = 0x0008 +}; + +RTTR_REGISTER +{ + enumeration_<E_Alignment>({ {"AlignLeft", E_Alignment::AlignLeft}, + {"AlignRight", E_Alignment::AlignRight}, + {"AlignHCenter", E_Alignment::AlignHCenter}, + {"AlignJustify", E_Alignment::AlignJustify} + }); +} +\endcode +You don't need to provide a name for the enum during the enumeration binding, because this is already done with the \ref declare_simple_types "registration process" for types. +The *key* is a std::string and the *value* is the enum value. +The class \ref enumeration contains several meta information about an enum with conversion functions between the value representation and its literal representation. + +How to use the enumeration class shows following example: +\code{.cpp} + type enum_type = type::get("E_Alignment"); + if (enum_type && enum_type.is_enumeration()) + { + enumeration enum_align = enum_type.get_enumeration(); + std::string key = enum_align.value_to_key(MyStruct::AlignHCenter); + std::cout << key; // prints "AlignHCenter" + + variant var = enum_align.key_to_value(key); + std::cout << var.get_value<MyStruct::E_Alignment>(); // prints "4"; + } +\endcode + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_variant 5. Variant + +The variant class acts as return value container for properties and methods. This class allows to store data of any type and convert between these types transparently. +It can hold one value at a time (using containers you can hold multiple types e.g. `std::vector<int>`). +Remark that the content is copied into the variant class. Even raw arrays (e.g. `int[10]`) are copied. +When you would like to avoid copies, use pointer types or wrap your type in a `std::reference_wrapper<T>` +A typical usage is the following example: + +\code{.cpp} + variant var; + var = 23; // copy integer + int x = var.to_int(); // x = 23 + + var = std:.string("Hello World"); // variant contains now a std::string + var = "Hello World"; // variant contains now a char[12] array + int y = var.to_int(); // y = 0, because invalid conversion + std::string text = var.to_string(); // text = "Hello World", char array to string converted + + var = "42"; // contains now char[3] array + std::cout << var.to_int(); // convert char array to integer and prints "42" + + int my_array[100]; + var = my_array; // copies the content of my_array into var + auto& arr = var.get_value<int[100]>(); // extracts the content of var by reference + + bool ret = var.can_convert<variant_array>();// return true + variant_array array = var.to_array(); // converts to variant_array, a helper class to get access to array values and other meta infos about array + std::size_t size = array.get_size(); // size = 100 + array.set_value(0, 42); // set the first value to 42 + std::cout << array.get_value(0); // prints 42 +\endcode + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_classes 6. Classes +For registering classes in RTTR you use a class called \ref class_. Its name is supposed to resemble the C++ keyword, to make it look more intuitive. +It has member functions for register \ref class_::constructor() "constructors", \ref class_::property() "properties", \ref class_::method() "methods" and \ref class_::enumeration() "enums". +These functions have the same interface and work in the same way like register the global symbols. +Every call to these member functions, will return a reference to *this*, in order to chain more registration calls. +The name of the class does not have to be provided, because this is already done with the \ref declare_simple_types "registration process" for types. + +Let's start with a simple example. Consider the following C++ class: + +\code{.cpp} + +// test class.h +class test_class{ +public: + test_class(int value) : m_value(value) {} + void print_value() const { std::cout << m_value; } +private: + int m_value; +RTTR_ENABLE() +}; + +RTTR_DECLARE_TYPE_VARIANTS(test_class); +\endcode + +The registration process is now done at global scope in the cpp file. + +\code{.cpp} +// test_class.cpp +using namespace rttr; + +RTTR_REGISTER +{ + class_<test_class>() + .constructor<int>() + .method("print_value", &test_class::print_value); + .property("value", &test_class::m_value); +} +\endcode +This will register the class `test_class` with a constructor that takes an integer as argument, a member function with the name `print_value` +and a property called `value`. + +The property name has be unique for this class type but derived classes can register another property with the same name again. +Member functions can be overloaded, so a method can be registered with an already existing name multiple times. However when there exist a method with the same name and signature, +then this function will not be registered and discarded. + +\subsubsection binding_types_overload_member_functions 6.1 Overloaded member functions + +When binding a overloaded member function, you have to disambiguate the member function pointer you pass to \ref class_::method() "method". +To do this, you can use `static_cast` or an ordinary C-style cast, to cast it to the right overload. + +The syntax for member function pointers if following: +\code{.cpp} +return-value (class-name::*)(arg1-type, arg2-type, ...) +\endcode + +Here's an example illlustrating this: + +\code{.cpp} +struct Foo +{ + void f(); + void f(int); +}; +\endcode + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .method("f", static_cast<void(Foo::*)(int)>(&Foo::f)); // C++ cast + .method("f", (void(Foo::*)())&Foo::f); // C style cast +} +\endcode + +This first overload of the function f is binded with a C++ cast. The second overload is binded with a C style cast. + +\subsubsection binding_types_class_ctor 6.2 Register constructor +\code{.cpp} + +RTTR allows your to register constructors for classes. +Because C++ doesn't allow to retrieve the member function pointer of a constructor you have to explicit specify all data types of a constructor. + +Consider following class with three constructors: +struct Foo +{ + Foo(); + Foo(int, double); + Foo(const std::string&); +}; +\endcode + +For registering these constructors you now have to specify every parameter as template parameter in the member function :\ref class_::constructor(). + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .constructor<>() + .constructor<int,double>() + .constructor<const std::string&>(); +} +\endcode + +When a constructor is registered a destructor is registered automatically. + +\subsubsection binding_types_class_props 6.3 Register class properties +Register a public property can be easily done, consider following class: + +\code{.cpp} +struct Foo +{ + int value; +}; +\endcode + +This class is registered like this: + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .property("value", &Foo::value); +} +\endcode + +With the \ref class_::property() "property()" member function you will bind the member variable `Foo::value` with read and write access. When you want a bind a property with `read-only` access, then this is also possible with \ref class_::property_readonly "property_readonly()" member function. + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .property_readonly("value", &Foo::value); +} +\endcode + +When you have a class and the property is declared in private scope, then you can still register this property when you insert the macro: \ref RTTR_REGISTER_FRIEND +inside the class. +\code{.cpp} +class Foo +{ + private: + int value; + + RTTR_REGISTER_FRIEND +}; +\endcode + +This will make this class a friend to the registration system. + +You can also register getter and setter functions and make them look as if they were a public data member. Consider the following class: +\code{.cpp} +class Foo +{ + public: + void set_value(int x) { m_value = x; } + int get_value() const { return m_value; } + + private: + int m_value; +}; +\endcode + +This is the registration code: + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .property("value", &Foo::get_value, &Foo::set_value); +} +\endcode + +This way, accessing the property will now call these functions, instead the property directly. Remark that the getter function needs the be `const`. + +The following sub sections will now show how to retrieve these informations for creating, invoking and setting properties of an instance of this class. + +\subsubsection binding_types_create_classes 6.4 Create/destroy of classes + +There are two options for creating/destroying a class. Option 1, use just the \ref type::create "type" interface or option 2 retrieve a \ref constructor +and \ref destructor object from the type class. + +\code{.cpp} +int main() +{ + // option 1 + type class_type = type::get("test_class"); + if (class_type) + { + variant obj = class_type.create({23}); + class_type.destroy(obj); + } + // option 2 + if (class_type) + { + constructor ctor = class_type.get_constructor({type::get<int>()}); + variant obj = ctor.invoke(23); + destructor dtor = class_type.get_destructor(); + dtor.invoke(obj); + } +} +\endcode +The objects which are constructed are created on the heap and stored as pointer in the variant object. + +\subsubsection binding_types_invoke_classes 6.5 Invoke member functions +Invoking a member function works in the same way like invoking global function. The only difference is, that you have to provide +the instance of the class. + +\code{.cpp} +int main() +{ + test_class obj(42); + type class_type = type::get("test_class"); + // option 1 + class_type.invoke("print_value", obj, {}); // print 42 + + // option 2 + method print_meth = class_type.get_method("print_value"); + print_meth.invoke(obj); // prints "42" +} +\endcode + +The invoke function also except to use variants. So when you create the object via the type constructor you can use the returned variant to invoke to method: + +\code{.cpp} +int main() +{ + variant obj_var = type::get("test_class").create({42}); + type::get("test_class").invoke("print_value", obj_var, {}); // print 42 +} +\endcode + +\subsubsection binding_types_set_get_property_classes 6.6 Set/Get property of a class +Properties can be also set an get in two ways. + +\code{.cpp} +int main() +{ + test_class obj(0); + type class_type = type::get("test_class"); + // option 1 + bool success = class_type.set_property_value("value", obj, 50); + std::cout << obj.m_value; // prints "50" + + // option 2 + property prop = class_type.get_property("value"); + success = prop.set_value(obj, 24); + std::cout << obj.m_value; // prints "24" +} +\endcode + +In difference to the global properties, a valid \ref type object and an instance (object) of the class is now needed to set and get the value. +It doesn't matter in what hierarchy level the object is. Or if its a pointer, an object on the stack or wrapped inside a variant. +RTTR will try to cast the given object to the class type where the property was registered to. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_meta_data 7. Metadata +Adding additional meta information to properties or methods can be very useful. +So for instance, it allows to add ToolTips or the information what kind of editor should be created in the GUI. +You can also tag certain properties to make only those available in a scripting engine which has a certain key set. + +The metadata consists of a *key*, which can be a std::string or an integer and a *value* which is a \ref variant. + +Please take a look at following example: + +\code{.cpp} +RTTR_REGISTER +{ + property_("value", value, { metadata(SCRIPTABLE, false), + metadata("Description", "This is a value.") }); +} +\endcode +This will register a global property named `"value"` with two metadata informations. +Both keys use the integer as data role, because the enum value will be implicit converted to an integer. + +And the following snippet shows, how to retrieve this information: +\code{.cpp} +int main() +{ + property prop = type::get_global_property("value"); + variant value = prop.get_metadata(SCRIPTABLE); + std::cout << value.get_value<bool>(); // prints "false" + + value = prop.get_metadata("Description"); + std::cout << value.get_value<int>(); // prints "This is a value." +} +\endcode + +Every \ref property, \ref method, \ref enumeration and \ref constructor can have metadata. + + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\subsection binding_types_policies 8. Policies +Sometimes it is necessary to adjust the default binding behaviour RTTR. Therefore policies were introduced. +At the moment only return value policies are implemented. +The default binding behaviour of RTTR is to return all values by copying the content in a variant. + +Currently implemented policies: +- \ref bind_bind_property_as_ptr "bind_property_as_ptr" +- \ref bind_return_reference_as_ptr "return_reference_as_ptr" +- \ref bind_discard_return_value "discard_return_value" + +\subsubsection bind_bind_property_as_ptr 8.1 bind_property_as_ptr +The motivation for this policy is to avoid expensive copies when returning a property. +When you have primitive data types like integer or doubles you are good to go with the standard binding behaviour. +But when you have big arrays, it would be a waste to always copy the content when retrieving or setting the value, therefore this policy was introduced. + +Example: +\code{.cpp} +struct Foo +{ + std::vector<int> vec; +}; +RTTR_REGISTER +{ + class_<Foo>() + .property("vec", &Foo::vec, bind_property_as_ptr); +} +\endcode + +\code{.cpp} +int main() +{ + Foo obj; + property vec_prop = type::get("Foo").get_property("vec"); + variant vec_value = prop.get_value(obj); + std::cout << value.is_type<std::vector<int>*>(); // prints "true" + + // not really necessary, but remark that now a std::vector<int>* is expected + prop.set_value(obj, vec_value); +} +\endcode + +\subsubsection bind_return_reference_as_ptr 8.2 return_reference_as_ptr +The motivation for this policy is the same like the \ref bind_bind_property_as_ptr "bind_property_as_ptr" policy. +When you really need to get a reference to the return value of a method call you have to use this policy, +otherwise the returned reference will be copied into the variant. + +Example: +\code{.cpp} +struct Foo +{ + std::string& get_text() { static text; return text; } +}; +RTTR_REGISTER +{ + class_<Foo>() + .method("get_text", &Foo::get_text, return_reference_as_ptr); +} +\endcode + +\code{.cpp} +int main() +{ + Foo obj; + method text_meth = type::get("Foo").get_method("get_text"); + variant vec_value = text_meth.invoke(obj); + std::cout << value.is_type<std::string*>(); // prints "true" + std::cout << text_meth.get_return_type().get_name(); // prints "std::string*" +} +\endcode + +\subsubsection bind_discard_return_value 8.3 discard_return_value + +Sometimes it is necessary that the client caller should ignore the return value of a method call. +Therefore this policies was introduced. + +Example: +\code{.cpp} +struct Foo +{ + int get_value() { return 42; } +}; +RTTR_REGISTER +{ + class_<Foo>() + .method("get_value", &Foo::get_value, discard_return_value); +} +\endcode + +\code{.cpp} +int main() +{ + Foo obj; + method text_meth = type::get("Foo").get_method("get_value"); + variant vec_value = text_meth.invoke(obj); + std::cout << value.is_type<void>(); // prints "true" + std::cout << text_meth.get_return_type().get_name(); // prints "void" +} +\endcode + +A closing hint: you can of course build your own policies in that way, that you build wrappers around your properties or methods and +then bind the wrapper instead of the original accessor. + +<!--/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////--> + +\par Go To Section... +- \ref page_tutorial +- \ref page_license +*/ + +} \ No newline at end of file diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..53153ba2 --- /dev/null +++ b/doc/index.html @@ -0,0 +1,6 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <meta http-equiv="refresh" content="0; URL=html/index.html"> + </head> +</html> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..fdff86d7 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,35 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +############################################# +# Include the core library, unit tests and +# the benchmarks +############################################# + +add_subdirectory (rttr) +add_subdirectory (test) +add_subdirectory (benchmark) diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt new file mode 100644 index 00000000..7569025d --- /dev/null +++ b/src/benchmark/CMakeLists.txt @@ -0,0 +1,55 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +project(benchmark) + +message(STATUS "Scanning " ${PROJECT_NAME} " module.") +message(STATUS "===========================") + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../) + +add_definitions(-DRTTR_DLL) + +set(SOURCE_FILES main.cpp + benchmark_rttr_cast.cpp + ) + +set(HEADER_FILES test_classes.h + benchmark_utils.h + ) + +if (USE_PCH) + activate_precompiled_headers("pch.h" SOURCE_FILES) +endif() + +add_executable(benchmark ${SOURCE_FILES} ${HEADER_FILES}) +target_link_libraries(benchmark rttr) +add_dependencies(benchmark rttr) +get_target_property(UnitTest_Exec unit_tests LOCATION) + +message(STATUS "Scanning " ${PROJECT_NAME} " module finished!") +message(STATUS "") \ No newline at end of file diff --git a/src/benchmark/benchmark_rttr_cast.cpp b/src/benchmark/benchmark_rttr_cast.cpp new file mode 100644 index 00000000..d0408492 --- /dev/null +++ b/src/benchmark/benchmark_rttr_cast.cpp @@ -0,0 +1,842 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_classes.h" +#include "benchmark_utils.h" + +#include <iostream> +#include <chrono> +#include <memory> + +#define ITEM_COUNT 50000 +#define RUN_COUNT 5 +#define COLUMN_0_WIDTH 41 +#define COLUMN_1_WIDTH 14 +#define COLUMN_2_WIDTH 14 +#define TABLE_WIDTH COLUMN_0_WIDTH + COLUMN_1_WIDTH + COLUMN_2_WIDTH + 8 + +using namespace rttr; +using namespace std; + + +struct BenchmarkResult +{ + BenchmarkResult(const string& title, chrono::high_resolution_clock::duration time) : _title(title), _timing(time) {} + string _title; + chrono::high_resolution_clock::duration _timing; +}; + +void printStats(const vector<BenchmarkResult>& timingResults) +{ + cout << "-------------------------------------------------------\n"; + for (vector<BenchmarkResult>::const_iterator itr = timingResults.begin(); + itr != timingResults.end(); + ++itr) + { + if (!(*itr)._title.empty()) + cout << (*itr)._title << ": " << chrono::duration_cast<chrono::milliseconds>((*itr)._timing).count() << " ms\n"; + } + cout << "-------------------------------------------------------\n"; +} + +void print_stats(const std::string& text, const chrono::high_resolution_clock::duration& time_1, + const chrono::high_resolution_clock::duration& time_2) +{ + std::cout << left(text, COLUMN_0_WIDTH) << " | " + << center(std::to_string(chrono::duration_cast<chrono::milliseconds>(time_1).count()) + " ms", COLUMN_1_WIDTH) << " | " + << center(std::to_string(chrono::duration_cast<chrono::milliseconds>(time_2).count()) + " ms", COLUMN_2_WIDTH) << " | " + << std::endl; +} + +template<typename T, typename... Args> +std::unique_ptr<T> make_unique_impl(Args&&... args) +{ + return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); +} + + +void bench_single_inheritance_level_1() +{ + vector<unique_ptr<ClassSingleBase> > vec; + vec.reserve(ITEM_COUNT * 5); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassSingle1A>()); + vec.push_back(make_unique_impl<ClassSingle1B>()); + vec.push_back(make_unique_impl<ClassSingle1C>()); + vec.push_back(make_unique_impl<ClassSingle1D>()); + vec.push_back(make_unique_impl<ClassSingle1E>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle1A* item = dynamic_cast<ClassSingle1A*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1B* item = dynamic_cast<ClassSingle1B*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1C* item = dynamic_cast<ClassSingle1C*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1D* item = dynamic_cast<ClassSingle1D*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1E* item = dynamic_cast<ClassSingle1E*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle1A* item = rttr_cast<ClassSingle1A*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1B* item = rttr_cast<ClassSingle1B*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1C* item = rttr_cast<ClassSingle1C*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1D* item = rttr_cast<ClassSingle1D*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle1E* item = rttr_cast<ClassSingle1E*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_single_inheritance_level_1" << std::endl; + + print_stats("downcasting to level 1", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_single_inheritance_level_3() +{ + vector<unique_ptr<ClassSingleBase> > vec; + vec.reserve(ITEM_COUNT * 5); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassSingle3A>()); + vec.push_back(make_unique_impl<ClassSingle3B>()); + vec.push_back(make_unique_impl<ClassSingle3C>()); + vec.push_back(make_unique_impl<ClassSingle3D>()); + vec.push_back(make_unique_impl<ClassSingle3E>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle3A* item = dynamic_cast<ClassSingle3A*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3B* item = dynamic_cast<ClassSingle3B*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3C* item = dynamic_cast<ClassSingle3C*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3D* item = dynamic_cast<ClassSingle3D*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3E* item = dynamic_cast<ClassSingle3E*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + vector<void*> result; + result.resize(vec.size()); + chrono::high_resolution_clock::duration elapsedTime_2; + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle3A* item = rttr_cast<ClassSingle3A*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3B* item = rttr_cast<ClassSingle3B*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3C* item = rttr_cast<ClassSingle3C*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3D* item = rttr_cast<ClassSingle3D*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle3E* item = rttr_cast<ClassSingle3E*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_single_inheritance_level_3" << std::endl; + + print_stats("downcasting to level 3", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_single_inheritance_level_6() +{ + vector<unique_ptr<ClassSingleBase> > vec; + vec.reserve(ITEM_COUNT * 5); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassSingle6A>()); + vec.push_back(make_unique_impl<ClassSingle6B>()); + vec.push_back(make_unique_impl<ClassSingle6C>()); + vec.push_back(make_unique_impl<ClassSingle6D>()); + vec.push_back(make_unique_impl<ClassSingle6E>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle6A* item = dynamic_cast<ClassSingle6A*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6B* item = dynamic_cast<ClassSingle6B*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6C* item = dynamic_cast<ClassSingle6C*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6D* item = dynamic_cast<ClassSingle6D*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6E* item = dynamic_cast<ClassSingle6E*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassSingle6A* item = rttr_cast<ClassSingle6A*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6B* item = rttr_cast<ClassSingle6B*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6C* item = rttr_cast<ClassSingle6C*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6D* item = rttr_cast<ClassSingle6D*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassSingle6E* item = rttr_cast<ClassSingle6E*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_single_inheritance_level_6" << std::endl; + + print_stats("downcasting to level 6", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_virtual_inheritance_level_1() +{ + vector<unique_ptr<ClassDiamondTop> > vec; + + vec.reserve(ITEM_COUNT * 3); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassDiamondLeft1>()); + vec.push_back(make_unique_impl<ClassDiamondMiddle1>()); + vec.push_back(make_unique_impl<ClassDiamondRight1>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft1* item = dynamic_cast<ClassDiamondLeft1*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle1* item = dynamic_cast<ClassDiamondMiddle1*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight1* item = dynamic_cast<ClassDiamondRight1*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft1* item = rttr_cast<ClassDiamondLeft1*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle1* item = rttr_cast<ClassDiamondMiddle1*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight1* item = rttr_cast<ClassDiamondRight1*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_virtual_inheritance_level_1" << std::endl; + + print_stats("downcasting to level 1 - virtual inheri.", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_virtual_inheritance_level_3() +{ + vector<unique_ptr<ClassDiamondTop> > vec; + + vec.reserve(ITEM_COUNT * 3); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassDiamondLeft3>()); + vec.push_back(make_unique_impl<ClassDiamondMiddle3>()); + vec.push_back(make_unique_impl<ClassDiamondRight3>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft3* item = dynamic_cast<ClassDiamondLeft3*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle3* item = dynamic_cast<ClassDiamondMiddle3*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight3* item = dynamic_cast<ClassDiamondRight3*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft3* item = rttr_cast<ClassDiamondLeft3*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle3* item = rttr_cast<ClassDiamondMiddle3*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight3* item = rttr_cast<ClassDiamondRight3*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_virtual_inheritance_level_3" << std::endl; + + print_stats("downcasting to level 3 - virtual inheri.", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_virtual_inheritance_level_6() +{ + vector<unique_ptr<ClassDiamondTop> > vec; + + vec.reserve(ITEM_COUNT * 3); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassDiamondLeft6>()); + vec.push_back(make_unique_impl<ClassDiamondMiddle6>()); + vec.push_back(make_unique_impl<ClassDiamondRight6>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft6* item = dynamic_cast<ClassDiamondLeft6*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle6* item = dynamic_cast<ClassDiamondMiddle6*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight6* item = dynamic_cast<ClassDiamondRight6*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassDiamondLeft6* item = rttr_cast<ClassDiamondLeft6*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondMiddle6* item = rttr_cast<ClassDiamondMiddle6*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + else if (ClassDiamondRight6* item = rttr_cast<ClassDiamondRight6*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_virtual_inheritance_level_6" << std::endl; + + print_stats("downcasting to level 6 - virtual inheri.", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_multiple_class_hierachy_end() +{ + vector<unique_ptr<ClassMultipleBaseA> > vec; + + vec.reserve(ITEM_COUNT * 7); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<FinalClass>()); + vec.push_back(make_unique_impl<ClassMultiple5A>()); + vec.push_back(make_unique_impl<FinalClass>()); + vec.push_back(make_unique_impl<ClassMultiple4A>()); + vec.push_back(make_unique_impl<FinalClass>()); + vec.push_back(make_unique_impl<ClassMultiple5A>()); + vec.push_back(make_unique_impl<FinalClass>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (FinalClass* item = dynamic_cast<FinalClass*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (int i = 0; i < ITEM_COUNT * 7; ++i) + { + if (FinalClass* item = rttr_cast<FinalClass*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_multiple_class_hierachy_end" << std::endl; + + print_stats("Cast from Base to Final - multi inheri.", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_cross_cast_1() +{ + vector<shared_ptr<ClassMultipleBaseA> > vec; + + vec.reserve(ITEM_COUNT * 2); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<FinalClass>()); + vec.push_back(make_unique_impl<ClassMultiple6A>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassMultiple1B* item = dynamic_cast<ClassMultiple1B*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassMultiple1B* item = rttr_cast<ClassMultiple1B*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_cross_cast_1" << std::endl; + + print_stats("crosscasting - 2 classes wide", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_cross_cast_2() +{ + vector<shared_ptr<ClassMultipleBaseA> > vec; + + vec.reserve(ITEM_COUNT * 2); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<FinalClass>()); + vec.push_back(make_unique_impl<ClassMultiple6A>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassMultiple1E* item = dynamic_cast<ClassMultiple1E*>(vec[i].get())) + { + result_reference[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (ClassMultiple1E* item = rttr_cast<ClassMultiple1E*>(vec[i].get())) + { + result[i] = reinterpret_cast<void*>(item); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_cross_cast_2" << std::endl; + + print_stats("crosscasting - 4 classes wide", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void bench_type_get_vs_typeid() +{ + vector<unique_ptr<ClassSingleBase> > vec; + + vec.reserve(ITEM_COUNT * 5); + for (int i = 0; i < ITEM_COUNT; ++i) + { + vec.push_back(make_unique_impl<ClassSingle6A>()); + vec.push_back(make_unique_impl<ClassSingle6B>()); + vec.push_back(make_unique_impl<ClassSingle6C>()); + vec.push_back(make_unique_impl<ClassSingle6D>()); + vec.push_back(make_unique_impl<ClassSingle6E>()); + } + + chrono::high_resolution_clock::duration elapsedTime_1; + vector<void*> result_reference; + result_reference.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (typeid(ClassSingle6A) == typeid(*vec[i])) + { + result_reference[i] = vec[i]->get_ptr(); + } + else if (typeid(ClassSingle6B) == typeid(*vec[i])) + { + result_reference[i] = vec[i]->get_ptr(); + } + else if (typeid(ClassSingle6C) == typeid(*vec[i])) + { + result_reference[i] = vec[i]->get_ptr(); + } + else if (typeid(ClassSingle6D) == typeid(*vec[i])) + { + result_reference[i] = vec[i]->get_ptr(); + } + else if (typeid(ClassSingle6E) == typeid(*vec[i])) + { + result_reference[i] = vec[i]->get_ptr(); + } + } + elapsedTime_1 += chrono::high_resolution_clock::now() - startTime; + } + elapsedTime_1 /= RUN_COUNT; + + chrono::high_resolution_clock::duration elapsedTime_2; + vector<void*> result; + result.resize(vec.size()); + for (int count = 0; count < RUN_COUNT; ++count) + { + const auto startTime = chrono::high_resolution_clock::now(); + for (std::size_t i = 0; i < vec.size(); ++i) + { + if (type::get<ClassSingle6A>() == type::get(*vec[i])) + { + result[i] = vec[i]->get_ptr(); + } + else if (type::get<ClassSingle6B>() == type::get(*vec[i])) + { + result[i] = vec[i]->get_ptr(); + } + else if (type::get<ClassSingle6C>() == type::get(*vec[i])) + { + result[i] = vec[i]->get_ptr(); + } + else if (type::get<ClassSingle6D>() == type::get(*vec[i])) + { + result[i] = vec[i]->get_ptr(); + } + else if (type::get<ClassSingle6E>() == type::get(*vec[i])) + { + result[i] = vec[i]->get_ptr(); + } + } + elapsedTime_2 += chrono::high_resolution_clock::now() - startTime; + } + + elapsedTime_2 /= RUN_COUNT; + + if (result != result_reference) + std::cout << "ERROR - Invalid rttr_cast - bench_type_get_vs_typeid" << std::endl; + + print_stats("BaseClass to Level 6 [typeid]", elapsedTime_1, elapsedTime_2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void start_rttr_cast_benchmark() +{ + std::cout << center("dynamic_cast vs. rttr_cast", TABLE_WIDTH) << std::endl; + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + std::cout << left("", COLUMN_0_WIDTH) << " | " + << center("[dynamic_cast]", COLUMN_1_WIDTH) << " | " + << center("[rttr_cast]", COLUMN_2_WIDTH) << " | " + << "\n"; + + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + + bench_single_inheritance_level_1(); + bench_single_inheritance_level_3(); + bench_single_inheritance_level_6(); + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + bench_virtual_inheritance_level_1(); + bench_virtual_inheritance_level_3(); + bench_virtual_inheritance_level_6(); + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + bench_multiple_class_hierachy_end(); + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + bench_cross_cast_1(); + bench_cross_cast_2(); + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; + bench_type_get_vs_typeid(); + std::cout << std::string(TABLE_WIDTH, '-') << "\n"; +} \ No newline at end of file diff --git a/src/benchmark/benchmark_utils.h b/src/benchmark/benchmark_utils.h new file mode 100644 index 00000000..0e37c4b8 --- /dev/null +++ b/src/benchmark/benchmark_utils.h @@ -0,0 +1,94 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_BENCH_UTILS_H__ +#define __RTTR_BENCH_UTILS_H__ + +#include <string> +#include <sstream> + +/* Convert double to string with specified number of places after the decimal. */ +std::string prd(const double x, const int decDigits) +{ + std::stringstream ss; + ss << std::fixed; + ss.precision(decDigits); // set # places after decimal + ss << x; + return ss.str(); +} + +std::string prd(const double x, const int decDigits, const int width) +{ + std::stringstream ss; + ss << std::fixed << std::right; + ss.fill(' '); // fill space around displayed # + ss.width(width); // set width around displayed # + ss.precision(decDigits); // set # places after decimal + ss << x; + return ss.str(); +} + +/*! Center-aligns string within a field of width w. Pads with blank spaces + to enforce alignment. */ +std::string center(const std::string& s, const std::size_t w) +{ + std::stringstream ss, spaces; + const std::size_t padding = (s.size() <= w) ? w - s.size() : s.size(); // count excess room to pad + for(std::size_t i = 0; i < padding / 2; ++i) + spaces << " "; + ss << spaces.str() << s << spaces.str(); // format with padding + if(padding>0 && padding%2!=0) // if odd #, add 1 space + ss << " "; + return ss.str(); +} + +/* Right-aligns string within a field of width w. Pads with blank spaces + to enforce alignment. */ +std::string right(const std::string& s, const std::size_t w) +{ + std::stringstream ss, spaces; + const std::size_t padding = (s.size() <= w) ? w - s.size() : s.size(); // count excess room to pad + for(std::size_t i=0; i<padding; ++i) + spaces << " "; + ss << spaces.str() << s; // format with padding + return ss.str(); +} + +/*! Left-aligns string within a field of width w. Pads with blank spaces + to enforce alignment. */ +std::string left(const std::string& s, const std::size_t w) +{ + std::stringstream ss, spaces; + const std::size_t padding = (s.size() <= w) ? w - s.size() : s.size(); // count excess room to pad + for(std::size_t i=0; i<padding; ++i) + spaces << " "; + ss << s << spaces.str(); // format with padding + return ss.str(); +} + + +#endif // __RTTR_BENCH_UTILS_H__ diff --git a/src/benchmark/main.cpp b/src/benchmark/main.cpp new file mode 100644 index 00000000..9e6160a2 --- /dev/null +++ b/src/benchmark/main.cpp @@ -0,0 +1,331 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_classes.h" + +#include <iostream> + +extern void start_rttr_cast_benchmark(); + +int main(int argc, char** argv) +{ + start_rttr_cast_benchmark(); + return 0; +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#if 0 + +void string_func(std::string& text) +{ + std::cout << text << std::endl; + text += " world"; +} + +void func(variant2& var) +{ + if (var.is_type<std::string&>()) + string_func(var.get_value<std::string&>()); +} + +using namespace rttr; + +struct Test +{ + Test() : value(23) + { + + } + Test(const Test& other) + { + std::cout << "copy ctor" << std::endl; + } + + Test(Test&& other) + { + std::cout << "move ctor" << std::endl; + } + + int value; +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(Test) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(Test) + +RTTR_DECLARE_TYPE(char[6]) +RTTR_DEFINE_TYPE(char[6]) +//RTTR_DEFINE_STANDARD_TYPE_VARIANTS(char[6]) + + + + + + + + + + + + + + + +class DataContainer +{ +public: + DataContainer() : m_data(nullptr), m_typeInfo(rttr::type::get<void>()) {} + + DataContainer(const DataContainer& other) : m_data(other.m_data), m_typeInfo(other.m_typeInfo) {} + DataContainer& operator=(const DataContainer& other) + { + m_data = other.m_data; + m_typeInfo = other.m_typeInfo; + return *this; + } + + template<typename T> + DataContainer(T&& value) + : m_data(reinterpret_cast<void*>(const_cast< typename std::add_pointer<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::type >(std::addressof(value)))), + m_typeInfo(rttr::type::get<typename std::decay<T>::type>()) + { + + } + + template<typename T> + bool is_type() const { return rttr::type::get<typename std::decay<T>::type>() == m_typeInfo; } + + template<typename T> + T getType(typename std::enable_if<std::is_pointer<T>::value>::type* = nullptr) const + { + T value = reinterpret_cast<T>(m_data); + return (value); + } + + template<typename T> + T getType(typename std::enable_if<!std::is_pointer<T>::value >::type* = nullptr) const + { + typedef typename std::remove_reference<T>::type PtrType; + PtrType* value = reinterpret_cast<PtrType*>(m_data); + return (*value); + } + +private: + void* m_data; + rttr::type m_typeInfo; +}; + +template<typename T> +struct Hello +{ + +}; + +template<> +struct Hello<char> +{}; + +template<> +struct Hello<char[6]> +{}; + +void foofunc(char hello[6]) +{ + +} + + +void myRealFunc(std::string& name) +{ + std::cout << name << std::endl; + name += "additional"; +} + +void myRealPointerFunc(Test* arg) +{ + + + arg->value = 12; +} + +void callFunc(const DataContainer& arg) +{ + if (arg.is_type<std::string&>()) + myRealFunc(arg.getType<std::string&>()); +} + +void callPointerFunc(const DataContainer& arg) +{ + if (arg.is_type<Test*>()) + myRealPointerFunc(arg.getType<Test*>()); +} + +std::string getValue(int foo) +{ + if (foo == 12) + return std::string("Hello"); + else + return std::string("NO"); +} +/* +DataContainer getter(const DataContainer& arg) +{ + if (arg.isOfType<int>()) + return getValue(arg.getType<int>()); + return + DataContainer(); +} +*/ + + +TEST_CASE("ReflectionTest", "[Basic]") +{ + int foo = 12; +#if 0 + DataContainer er; + { + std::string t = "HELLO WOLRD"; + DataContainer foo(std::move(t)); + } + + callFunc(std::string("hello")); +#endif + //REQUIRE(Factorial(1) == 1); + //REQUIRE(Factorial(2) == 2); + //REQUIRE(Factorial(3) == 6); + //REQUIRE(Factorial(10) == 3628800); +} + +#endif + + diff --git a/src/benchmark/pch.h b/src/benchmark/pch.h new file mode 100644 index 00000000..ed29cc63 --- /dev/null +++ b/src/benchmark/pch.h @@ -0,0 +1,20 @@ +// std stuff +#include <map> +#include <string> +#include <vector> +#include <set> +#include <list> +#include <iostream> +#include <limits> +#include <sstream> +#include <fstream> +#include <iomanip> +#include <tuple> +#include <algorithm> +#include <cassert> +#include <climits> +#include <iosfwd> +#include <memory> +#include <type_traits> + +#include <chrono> diff --git a/src/benchmark/test_classes.h b/src/benchmark/test_classes.h new file mode 100644 index 00000000..eb5120b3 --- /dev/null +++ b/src/benchmark/test_classes.h @@ -0,0 +1,169 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TESTCLASSES_H__ +#define __RTTR_TESTCLASSES_H__ + +#include <rttr/type> + +#define CLASS(CLASS_NAME) struct CLASS_NAME { virtual ~CLASS_NAME() {} RTTR_ENABLE() virtual int getType() { return 0; } int dummyIntValue; }; \ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS_NAME) + +#define CLASS_INHERIT(CLASS1, CLASS2) struct CLASS1 : CLASS2 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2) double dummyDoubleValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_VIRTUAL_INHERIT(CLASS1, CLASS2) struct CLASS1 : virtual CLASS2 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2) double dummyDoubleValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_MULTI_INHERIT_2(CLASS1, CLASS2, CLASS3) struct CLASS1 : CLASS2, CLASS3 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2, CLASS3) bool dummyBoolValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_MULTI_INHERIT_3(CLASS1, CLASS2, CLASS3, CLASS4) struct CLASS1 : CLASS2, CLASS3, CLASS4 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2, CLASS3, CLASS4) bool dummyBoolValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_MULTI_INHERIT_5(CLASS1, CLASS2, CLASS3, CLASS4, CLASS5, CLASS6) struct CLASS1 : CLASS2, CLASS3, CLASS4, CLASS5, CLASS6 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2, CLASS3, CLASS4, CLASS5, CLASS6) bool dummyBoolValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and is 5 classes wide; +// only single inheritance +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassSingleBase) +CLASS_INHERIT(ClassSingle1A, ClassSingleBase) +CLASS_INHERIT(ClassSingle2A, ClassSingle1A) +CLASS_INHERIT(ClassSingle3A, ClassSingle2A) +CLASS_INHERIT(ClassSingle4A, ClassSingle3A) +CLASS_INHERIT(ClassSingle5A, ClassSingle4A) +CLASS_INHERIT(ClassSingle6A, ClassSingle5A) + +CLASS_INHERIT(ClassSingle1B, ClassSingleBase) +CLASS_INHERIT(ClassSingle2B, ClassSingle1B) +CLASS_INHERIT(ClassSingle3B, ClassSingle2B) +CLASS_INHERIT(ClassSingle4B, ClassSingle3B) +CLASS_INHERIT(ClassSingle5B, ClassSingle4B) +CLASS_INHERIT(ClassSingle6B, ClassSingle5B) + +CLASS_INHERIT(ClassSingle1C, ClassSingleBase) +CLASS_INHERIT(ClassSingle2C, ClassSingle1C) +CLASS_INHERIT(ClassSingle3C, ClassSingle2C) +CLASS_INHERIT(ClassSingle4C, ClassSingle3C) +CLASS_INHERIT(ClassSingle5C, ClassSingle4C) +CLASS_INHERIT(ClassSingle6C, ClassSingle5C) + +CLASS_INHERIT(ClassSingle1D, ClassSingleBase) +CLASS_INHERIT(ClassSingle2D, ClassSingle1D) +CLASS_INHERIT(ClassSingle3D, ClassSingle2D) +CLASS_INHERIT(ClassSingle4D, ClassSingle3D) +CLASS_INHERIT(ClassSingle5D, ClassSingle4D) +CLASS_INHERIT(ClassSingle6D, ClassSingle5D) + +CLASS_INHERIT(ClassSingle1E, ClassSingleBase) +CLASS_INHERIT(ClassSingle2E, ClassSingle1E) +CLASS_INHERIT(ClassSingle3E, ClassSingle2E) +CLASS_INHERIT(ClassSingle4E, ClassSingle3E) +CLASS_INHERIT(ClassSingle5E, ClassSingle4E) +CLASS_INHERIT(ClassSingle6E, ClassSingle5E) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and 5 classes wide; +// At the end is a final class which uses multiple inheritance to combine all classes +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassMultipleBaseA) +CLASS_INHERIT(ClassMultiple1A, ClassMultipleBaseA) +CLASS_INHERIT(ClassMultiple2A, ClassMultiple1A) +CLASS_INHERIT(ClassMultiple3A, ClassMultiple2A) +CLASS_INHERIT(ClassMultiple4A, ClassMultiple3A) +CLASS_INHERIT(ClassMultiple5A, ClassMultiple4A) +CLASS_INHERIT(ClassMultiple6A, ClassMultiple5A) + +CLASS(ClassMultipleBaseB) +CLASS_INHERIT(ClassMultiple1B, ClassMultipleBaseB) +CLASS_INHERIT(ClassMultiple2B, ClassMultiple1B) +CLASS_INHERIT(ClassMultiple3B, ClassMultiple2B) +CLASS_INHERIT(ClassMultiple4B, ClassMultiple3B) +CLASS_INHERIT(ClassMultiple5B, ClassMultiple4B) +CLASS_INHERIT(ClassMultiple6B, ClassMultiple5B) + +CLASS(ClassMultipleBaseC) +CLASS_INHERIT(ClassMultiple1C, ClassMultipleBaseC) +CLASS_INHERIT(ClassMultiple2C, ClassMultiple1C) +CLASS_INHERIT(ClassMultiple3C, ClassMultiple2C) +CLASS_INHERIT(ClassMultiple4C, ClassMultiple3C) +CLASS_INHERIT(ClassMultiple5C, ClassMultiple4C) +CLASS_INHERIT(ClassMultiple6C, ClassMultiple5C) + +CLASS(ClassMultipleBaseD) +CLASS_INHERIT(ClassMultiple1D, ClassMultipleBaseD) +CLASS_INHERIT(ClassMultiple2D, ClassMultiple1D) +CLASS_INHERIT(ClassMultiple3D, ClassMultiple2D) +CLASS_INHERIT(ClassMultiple4D, ClassMultiple3D) +CLASS_INHERIT(ClassMultiple5D, ClassMultiple4D) +CLASS_INHERIT(ClassMultiple6D, ClassMultiple5D) + +CLASS(ClassMultipleBaseE) +CLASS_INHERIT(ClassMultiple1E, ClassMultipleBaseE) +CLASS_INHERIT(ClassMultiple2E, ClassMultiple1E) +CLASS_INHERIT(ClassMultiple3E, ClassMultiple2E) +CLASS_INHERIT(ClassMultiple4E, ClassMultiple3E) +CLASS_INHERIT(ClassMultiple5E, ClassMultiple4E) +CLASS_INHERIT(ClassMultiple6E, ClassMultiple5E) + +CLASS_MULTI_INHERIT_5(FinalClass, ClassMultiple6A, ClassMultiple6B, ClassMultiple6C, ClassMultiple6D, ClassMultiple6E) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and 3 classes wide; +// At the end is a final class which uses multiple inheritance to combine all classes +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassDiamondTop) + +CLASS_VIRTUAL_INHERIT(ClassDiamondLeft1, ClassDiamondTop) +CLASS_INHERIT(ClassDiamondLeft2, ClassDiamondLeft1) +CLASS_INHERIT(ClassDiamondLeft3, ClassDiamondLeft2) +CLASS_INHERIT(ClassDiamondLeft4, ClassDiamondLeft3) +CLASS_INHERIT(ClassDiamondLeft5, ClassDiamondLeft4) +CLASS_INHERIT(ClassDiamondLeft6, ClassDiamondLeft5) + +CLASS_VIRTUAL_INHERIT(ClassDiamondMiddle1, ClassDiamondTop) +CLASS_INHERIT(ClassDiamondMiddle2, ClassDiamondMiddle1) +CLASS_INHERIT(ClassDiamondMiddle3, ClassDiamondMiddle2) +CLASS_INHERIT(ClassDiamondMiddle4, ClassDiamondMiddle3) +CLASS_INHERIT(ClassDiamondMiddle5, ClassDiamondMiddle4) +CLASS_INHERIT(ClassDiamondMiddle6, ClassDiamondMiddle5) + +CLASS_VIRTUAL_INHERIT(ClassDiamondRight1, ClassDiamondTop) +CLASS_INHERIT(ClassDiamondRight2, ClassDiamondRight1) +CLASS_INHERIT(ClassDiamondRight3, ClassDiamondRight2) +CLASS_INHERIT(ClassDiamondRight4, ClassDiamondRight3) +CLASS_INHERIT(ClassDiamondRight5, ClassDiamondRight4) +CLASS_INHERIT(ClassDiamondRight6, ClassDiamondRight5) + +CLASS_MULTI_INHERIT_3(ClassDiamondFinal, ClassDiamondLeft5, ClassDiamondMiddle5, ClassDiamondRight5) + +#endif // __RTTR_TESTCLASSES_H__ diff --git a/src/rttr/CMakeLists.txt b/src/rttr/CMakeLists.txt new file mode 100644 index 00000000..f2e816cf --- /dev/null +++ b/src/rttr/CMakeLists.txt @@ -0,0 +1,140 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +project(rttr) + +message(STATUS "Scanning " ${PROJECT_NAME} " module.") +message(STATUS "===========================") + +loadFolder("rttr" HPP_FILES SRC_FILES) + +if (CREATE_UNITY_BUILD) + generateUnityFile(UnityBuild SRC_FILES) +endif() + +if (USE_PCH) + activate_precompiled_headers("pch.h" SRC_FILES) +endif() + +if (MSVC) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + add_library(rttr SHARED ${UnityBuild} ${SRC_FILES} ${HPP_FILES} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +else() + add_library(rttr SHARED ${UnityBuild} ${SRC_FILES} ${HPP_FILES}) +endif() + +install(TARGETS rttr EXPORT rttr_lib + RUNTIME DESTINATION ${RTTR_BIN_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_BIN_INSTALL_DIR} + ARCHIVE DESTINATION ${RTTR_LIB_INSTALL_DIR}) + +# install the shared object also into the lib directory +install(TARGETS rttr + RUNTIME DESTINATION ${RTTR_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_LIB_INSTALL_DIR}) + +install(EXPORT rttr_lib + DESTINATION cmake + FILE rttr-config.cmake) + +target_include_directories(rttr PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../> + $<INSTALL_INTERFACE:include> +) + +#set_target_properties(rttr PROPERTIES OUTPUT_NAME foo) +target_compile_definitions(rttr PRIVATE RTTR_DLL_EXPORTS) +target_compile_definitions(rttr PUBLIC RTTR_DLL ) + +set_target_properties(rttr PROPERTIES VERSION ${RTTR_VERSION} SOVERSION ${RTTR_VERSION}) + +if (BUILD_STATIC) + add_library(rttr-static STATIC ${UnityBuild} ${SRC_FILES} ${HPP_FILES}) + + install(TARGETS rttr-static EXPORT rttr_lib + ARCHIVE DESTINATION ${RTTR_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_LIB_INSTALL_DIR}) + + target_include_directories(rttr-static PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../> + $<INSTALL_INTERFACE:include>) + + set_target_properties(rttr-static PROPERTIES VERSION ${RTTR_VERSION} SOVERSION ${RTTR_VERSION}) +endif() + +if (BUILD_WITH_STATIC_RUNTIME_LIBS) + if (MSVC) + add_library(rttr-stdstatic SHARED ${UnityBuild} ${SRC_FILES} ${HPP_FILES} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + else() + add_library(rttr-stdstatic SHARED ${UnityBuild} ${SRC_FILES} ${HPP_FILES}) + endif() + + add_library(rttr-static-stdstatic STATIC ${UnityBuild} ${SRC_FILES} ${HPP_FILES}) + + install(TARGETS rttr-stdstatic EXPORT rttr_lib + RUNTIME DESTINATION ${RTTR_BIN_INSTALL_DIR} + ARCHIVE DESTINATION ${RTTR_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_LIB_INSTALL_DIR}) + + install(TARGETS rttr-stdstatic + RUNTIME DESTINATION ${RTTR_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_LIB_INSTALL_DIR}) + + install(TARGETS rttr-static-stdstatic EXPORT rttr_lib + ARCHIVE DESTINATION ${RTTR_LIB_INSTALL_DIR} + LIBRARY DESTINATION ${RTTR_LIB_INSTALL_DIR}) + + target_include_directories(rttr-stdstatic PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../> + $<INSTALL_INTERFACE:include>) + + target_include_directories(rttr-static-stdstatic PUBLIC + $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../> + $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../> + $<INSTALL_INTERFACE:include>) + + set_target_properties(rttr-stdstatic PROPERTIES VERSION ${RTTR_VERSION} SOVERSION ${RTTR_VERSION}) + set_target_properties(rttr-static-stdstatic PROPERTIES VERSION ${RTTR_VERSION} SOVERSION ${RTTR_VERSION}) + + if (MSVC) + target_compile_options(rttr-stdstatic PUBLIC "/MT$<$<CONFIG:Debug>:d>") + target_compile_options(rttr-static-stdstatic PUBLIC "/MT$<$<CONFIG:Debug>:d>") + elseif(CMAKE_COMPILER_IS_GNUCXX) + set_target_properties(rttr-stdstatic PROPERTIES LINK_FLAGS "-static-libstdc++") + set_target_properties(rttr-static-stdstatic PROPERTIES LINK_FLAGS "-static-libstdc++") + else() + message(SEND_ERROR "Do now know how to statically link against the standard library with this compiler.") + endif() +endif() + +message(STATUS "Scanning " ${PROJECT_NAME} " module finished!") +message(STATUS "") \ No newline at end of file diff --git a/src/rttr/base/core_prerequisites.h b/src/rttr/base/core_prerequisites.h new file mode 100644 index 00000000..1c91f98c --- /dev/null +++ b/src/rttr/base/core_prerequisites.h @@ -0,0 +1,185 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_CORE_PREREQUISITES_H__ +#define __RTTR_CORE_PREREQUISITES_H__ + +#include "rttr/base/version.h" + +namespace rttr +{ + +#define RTTR_PLATFORM_WINDOWS 1 +#define RTTR_PLATFORM_LINUX 2 + +#define RTTR_COMPILER_MSVC 1 +#define RTTR_COMPILER_GNUC 2 + +#define RTTR_ENDIAN_LITTLE 1 +#define RTTR_ENDIAN_BIG 2 + +#define RTTR_ARCH_32 1 +#define RTTR_ARCH_64 2 + +///////////////////////////////////////////////////////////////////////////////////////// +// Platform +///////////////////////////////////////////////////////////////////////////////////////// +#if defined( __WIN32__ ) || defined( _WIN32 ) +# define RTTR_PLATFORM RTTR_PLATFORM_WINDOWS +#else +# define RTTR_PLATFORM RTTR_PLATFORM_LINUX +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +// Compiler +///////////////////////////////////////////////////////////////////////////////////////// +#if defined( __GNUC__ ) +# define RTTR_COMPILER RTTR_COMPILER_GNUC +# define RTTR_COMP_VER (((__GNUC__)*1000) + \ + (__GNUC_MINOR__*100) + \ + __GNUC_PATCHLEVEL__) +#elif defined( _MSC_VER ) +# define RTTR_COMPILER RTTR_COMPILER_MSVC +# define RTTR_COMP_VER _MSC_VER +#else +# error "No known compiler. Abort! Abort!" +#endif + + +///////////////////////////////////////////////////////////////////////////////////////// +// Architecture +///////////////////////////////////////////////////////////////////////////////////////// +#if defined(__x86_64__) || defined(_M_X64) || defined(__powerpc64__) || defined(__alpha__) ||\ + defined(__ia64__) || defined(__s390__) || defined(__s390x__) +# define RTTR_ARCH_TYPE RTTR_ARCH_64 +#else +# define RTTR_ARCH_TYPE RTTR_ARCH_32 +#endif + +#if RTTR_COMPILER == RTTR_COMPILER_MSVC +# define RTTR_INLINE inline +# define RTTR_FORCE_INLINE __forceinline +#elif RTTR_COMPILER == RTTR_COMPILER_GNUC +# define RTTR_INLINE inline +# define RTTR_FORCE_INLINE inline __attribute__((always_inline)) +#else +# define RTTR_INLINE inline +# define RTTR_FORCE_INLINE inline // no force inline for other platforms possible +#endif + + +///////////////////////////////////////////////////////////////////////////////////////// +// Compiler specific cmds for export and import code to DLL +///////////////////////////////////////////////////////////////////////////////////////// +#if RTTR_COMPILER == RTTR_COMPILER_MSVC || __MINGW32__ || __CYGWIN__ +# define RTTR_HELPER_DLL_IMPORT __declspec( dllimport ) +# define RTTR_HELPER_DLL_EXPORT __declspec( dllexport ) +# define RTTR_HELPER_DLL_LOCAL +#elif RTTR_COMPILER == RTTR_COMPILER_GNUC +# if RTTR_COMP_VER >= 4000 +# define RTTR_HELPER_DLL_IMPORT __attribute__ ((visibility ("default"))) +# define RTTR_HELPER_DLL_EXPORT __attribute__ ((visibility ("default"))) +# define RTTR_HELPER_DLL_LOCAL __attribute__ ((visibility ("hidden"))) +# else +# define RTTR_HELPER_DLL_IMPORT +# define RTTR_HELPER_DLL_EXPORT +# define RTTR_HELPER_DLL_LOCAL +# endif +#else +# error "Do not know how to export classes for this platform" +#endif + +#ifdef RTTR_DLL // compiled as a DLL +# ifdef RTTR_DLL_EXPORTS // defined if we are building the DLL +# define RTTR_API RTTR_HELPER_DLL_EXPORT +# else +# define RTTR_API RTTR_HELPER_DLL_IMPORT +# endif +# define RTTR_LOCAL RTTR_HELPER_DLL_LOCAL +#else // it's a static lib. +# define RTTR_API +# define RTTR_LOCAL +#endif + +///////////////////////////////////////////////////////////////////////////////////////// +// Integer formats of fixed bit width +///////////////////////////////////////////////////////////////////////////////////////// +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef short int16; +typedef char int8; +typedef int int32; + +///////////////////////////////////////////////////////////////////////////////////////// +// Disable some MSVC compile warnings +///////////////////////////////////////////////////////////////////////////////////////// +#if RTTR_COMPILER == RTTR_COMPILER_MSVC +// Save warnings state +# pragma warning (push) +// Turn off warnings generated by long std templates +// This warns about truncation to 255 characters in debug/browse info +# pragma warning (disable : 4786) + +// Turn off warnings generated by long std templates +// This warns about truncation to 255 characters in debug/browse info +# pragma warning (disable : 4503) + +// disable: "<type> needs to have dll-interface to be used by clients' +// Happens on STL member variables which are not public therefore is ok +# pragma warning (disable : 4251) + +// disable: "non dll-interface class used as base for dll-interface class" +// Happens when deriving from Singleton because bug in compiler ignores +// template export +# pragma warning (disable : 4275) + +// disable: "C++ Exception Specification ignored" +// This is because MSVC 6 did not implement all the C++ exception +// specifications in the ANSI C++ draft. +# pragma warning( disable : 4290 ) + +// disable: "no suitable definition provided for explicit template +// instantiation request" Occurs in VC7 for no justifiable reason on all +// #includes of Singleton +# pragma warning( disable: 4661) + +// disable: deprecation warnings when using CRT calls in VC8 +// These show up on all C runtime lib code in VC8, disable since they clutter +// the warnings with things we may not be able to do anything about (e.g. +// generated code from nvparse etc). I doubt very much that these calls +// will ever be actually removed from VC anyway, it would break too much code. +# pragma warning( disable: 4996) + +// disable: "unreferenced formal parameter" +// Many versions of VC have bugs which generate this error in cases where they shouldn't +# pragma warning (disable : 4100) +#endif + +} // end namespace rttr + +#endif // __RTTR_CORE_PREREQUISITES_H__ \ No newline at end of file diff --git a/src/rttr/base/version.h.in b/src/rttr/base/version.h.in new file mode 100644 index 00000000..9c7ef5cd --- /dev/null +++ b/src/rttr/base/version.h.in @@ -0,0 +1,37 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_VERSION_H__ +#define __RTTR_VERSION_H__ + +#define RTTR_VERSION_MAJOR @RTTR_VERSION_MAJOR@ +#define RTTR_VERSION_MINOR @RTTR_VERSION_MINOR@ +#define RTTR_VERSION_PATCH @RTTR_VERSION_PATCH@ +#define RTTR_VERSION @RTTR_VERSION_CALC@ +#define RTTR_VERSION_STR "@RTTR_VERSION_STR@" + +#endif // __RTTR_VERSION_H__ \ No newline at end of file diff --git a/src/rttr/constructor.cpp b/src/rttr/constructor.cpp new file mode 100644 index 00000000..9fcbb71c --- /dev/null +++ b/src/rttr/constructor.cpp @@ -0,0 +1,222 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/constructor.h" +#include "rttr/detail/constructor_container_base.h" +#include "rttr/detail/argument.h" + +#include <utility> + + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +constructor::constructor(const detail::constructor_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool constructor::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +constructor::operator bool() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type constructor::get_instanciated_type() const +{ + if (is_valid()) + return _container->get_instanciated_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type constructor::get_declaring_type() const +{ + if (is_valid()) + return _container->get_declaring_type(); + else + return impl::get_invalid_type(); +} + + +///////////////////////////////////////////////////////////////////////////////////////// + +string constructor::get_signature() const +{ + if (is_valid()) + return _container->get_signature(); + else + return string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<type> constructor::get_parameter_types() const +{ + if (is_valid()) + return _container->get_parameter_types(); + else + return vector<type>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::get_metadata(int key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::get_metadata(const std::string& key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke() const +{ + if (is_valid()) + return _container->invoke(); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1) const +{ + if (is_valid()) + return _container->invoke(arg1); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1, detail::argument arg2) const +{ + if (is_valid()) + return _container->invoke(arg1, arg2); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3) const +{ + if (is_valid()) + return _container->invoke(arg1, arg2, arg3); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4) const +{ + if (is_valid()) + return _container->invoke(arg1, arg2, arg3, arg4); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5) const +{ + if (is_valid()) + return _container->invoke(arg1, arg2, arg3, arg4, arg5); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5, detail::argument arg6) const +{ + if (is_valid()) + return _container->invoke(arg1, arg2, arg3, arg4, arg5, arg6); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor::invoke_variadic(std::vector<detail::argument> args) const +{ + if (is_valid()) + return _container->invoke_variadic(args); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool constructor::operator==(const constructor& other) const +{ + return (_container == other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool constructor::operator!=(const constructor& other) const +{ + return (_container != other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + + +} // end namespace rttr diff --git a/src/rttr/constructor.h b/src/rttr/constructor.h new file mode 100644 index 00000000..4147cfa9 --- /dev/null +++ b/src/rttr/constructor.h @@ -0,0 +1,274 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ +#ifndef __RTTR_CONSTRUCTOR_H__ +#define __RTTR_CONSTRUCTOR_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <initializer_list> +#include <string> +#include <vector> + +namespace rttr +{ + +class variant; +class type; + +namespace detail +{ +class constructor_container_base; +class argument; +} + +/*! + * The \ref constructor class provides several meta information about a constructor and can be invoked. + * + * A instance of a constructor class can only be obtained from the \ref type class. + * See \ref type::get_constructor() and \ref type::get_constructors(). + * + * For registration a constructor, nested inside a class, see \ref class_::constructor() and for global constructors see \ref constructor_. + * + * Meta Information + * ---------------- + * A \ref constructor has a signature (\ref get_signature()) and instantiate exactly one type (\ref get_instanciated_type()). + * With \ref get_parameter_types() you retrieve all type objects of the parameters for this constructor. + * When the \ref constructor was declared inside a class, then \ref get_declaring_type() can be used to obtain the type of this class. + * + * The method can be invoked with \ref invoke(); When its not a \ref is_static "static method" you have to provide a class instance to invoke the method. + * This instance can be the raw type on the stack; the current hierarchy level doesn't matter. It can be also a raw pointer to the object or + * a \ref variant which contains the instance, again as pointer or stack object. + * + * Another way to invoke a method is to use the \ref type class through \ref type::create(). + * + * Copying and Assignment + * ---------------------- + * A \ref constructor object is lightweight and can be copied by value. However, each copy will refer to the same underlying constructor. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + constructor string_ctor == type::get("std::string").get_constructor({type::get("const char*")}); + + variant my_string = string_ctor.invoke({"Hello World"}); // returns an ptr to the object on the heap + + std::cout << my_string.get_value<std::string*>()->c_str() << std::endl; // prints 'Hello World' + + // don't forget to destroy the instance + type::get("std::string").get_destructor().invoke(my_string); + + my_string.is_valid(); // yield to false +\endcode + * + * \see method, property, enumeration, destructor and type + */ +class RTTR_API constructor +{ + public: + /*! + * \brief Returns true if this constructor is valid, otherwise false. + * + * \return True if this constructor is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this constructor is valid or not. + * + * \return True if this constructor is valid, otherwise false. + */ + operator bool() const; + + /*! + * Returns the type object of the instantiated type. + * + * \return The instantiated type. + */ + type get_instanciated_type() const; + + /*! + * \brief Returns the \ref type of the class or struct that declares this \ref constructor. + * + * \remark When this constructor does not belong to a class (i.e. is a primitive type) it will return an invalid type object. + * When this constructor is not valid, this function will return an invalid type object (see \ref type::is_valid). + * + * \return \ref type "Type" of the declaring class/struct for this enumeration. + */ + type get_declaring_type() const; + + /*! + * \brief Returns the signature of this constructor as readable string. + * + * \return The signature as readable string. + */ + std::string get_signature() const; + + /*! + * \brief Returns a list of type objects representing the number, order + * and type of the parameters for this constructor. + * + * \return A list representing the parameters of this constructor. + */ + std::vector<type> get_parameter_types() const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(int key) const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(const std::string& key) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke() const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1, detail::argument arg2) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * + * \remark Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke(detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5, detail::argument arg6) const; + + /*! + * \brief Invokes the constructor of type returned by \ref get_instanciated_type(). + * The instance will always be created on the heap and will be returned as variant object. + * Use this method when you need to instantiate a constructor with more then 6 parameters. + * + * \remark Using this invoke function is slower, then specifying the arguments directly. + * Returns an invalid \ref variant object (see \ref variant::is_valid), when the arguments does + * not match the parameters of the underlying constructor. + * + * \return An instance of the type \ref get_instanciated_type(). + */ + variant invoke_variadic(std::vector<detail::argument> args) const; + + /*! + * \brief Returns true if this constructor is the same like the \p other. + * + * \return True if both constructors are equal, otherwise false. + */ + bool operator==(const constructor& other) const; + + /*! + * Returns true if this constructor is the not the same like the \p other. + * + * \return True if both constructors are different, otherwise false. + */ + bool operator!=(const constructor& other) const; + + private: + friend class type; // to prevent creation of this class + constructor(const detail::constructor_container_base* container = nullptr); + private: + const detail::constructor_container_base* _container; +}; + +} // end namespace rttr + +#endif // __RTTR_CONSTRUCTOR_H__ diff --git a/src/rttr/destructor.cpp b/src/rttr/destructor.cpp new file mode 100644 index 00000000..54c3d4a5 --- /dev/null +++ b/src/rttr/destructor.cpp @@ -0,0 +1,92 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/destructor.h" +#include "rttr/detail/destructor_container_base.h" + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +destructor::destructor(const detail::destructor_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool destructor::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +destructor::operator bool() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type destructor::get_destructed_type() const +{ + if (is_valid()) + return _container->get_destructed_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void destructor::invoke(variant& obj) const +{ + if (is_valid()) + _container->invoke(obj); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool destructor::operator==(const destructor& other) const +{ + return (_container == other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool destructor::operator!=(const destructor& other) const +{ + return (_container != other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/destructor.h b/src/rttr/destructor.h new file mode 100644 index 00000000..1d025a04 --- /dev/null +++ b/src/rttr/destructor.h @@ -0,0 +1,127 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_DESTRUCTOR_H__ +#define __RTTR_DESTRUCTOR_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <string> + +namespace rttr +{ + +class variant; +class type; + +namespace detail +{ +class destructor_container_base; +} + +/*! + * The \ref destructor class provides a destructor for registered types. + * + * A instance of a destructor class can only be obtained from the \ref type class. + * See \ref type::get_destructor(). + * + * Copying and Assignment + * ---------------------- + * A \ref destructor object is lightweight and can be copied by value. However, each copy will refer to the same underlying destructor. + * + * Typical Usage + * ---------------------- + * + * +\code{.cpp} + constructor string_ctor == type::get("std::string").get_constructor({type::get("const char*")}); + + variant my_string = string_ctor.invoke({"Hello World"}); // returns an ptr to the object on the heap + + type::get("std::string").get_destructor().invoke(my_string); + + my_string.is_valid(); // yield to false +\endcode + * + * \see method, property, enumeration, constructor and type + */ +class RTTR_API destructor +{ + public: + /*! + * \brief Returns true whether this destructor object is valid; otherwise false. + * + * \return Returns true when the destructor is valid; otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this destructor is valid or not. + * + * \return True if this destructor is valid, otherwise false. + */ + operator bool() const; + + /*! + * \brief Returns the rttr::type for which this destructor can delete objects. + * + * \return The type of this destructor. + */ + type get_destructed_type() const; + + /*! + * \brief Destroys the given object \p obj. + * + * \remark When the \p obj could be destroyed the given \p obj is invalid after calling this method; + * Otherwise it is still valid. + */ + void invoke(variant& obj) const; + + /*! + * \brief Returns true if this destructor is the same like the \p other. + * + * \return True if both destructors are equal, otherwise false. + */ + bool operator==(const destructor& other) const; + + /*! + * Returns true if this destructor is the not the same like the \p other. + * + * \return True if both destructors are different, otherwise false. + */ + bool operator!=(const destructor& other) const; + + private: + friend class type; // to prevent creation of this class + destructor(const detail::destructor_container_base* container = nullptr); + private: + const detail::destructor_container_base* _container; +}; + +} // end namespace rttr + +#endif // __RTTR_DESTRUCTOR_H__ diff --git a/src/rttr/detail/accessor_type.h b/src/rttr/detail/accessor_type.h new file mode 100644 index 00000000..47f152af --- /dev/null +++ b/src/rttr/detail/accessor_type.h @@ -0,0 +1,124 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ACCESSOR_TYPE_H__ +#define __RTTR_ACCESSOR_TYPE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/function_traits.h" +#include "rttr/detail/misc_type_traits.h" + +#include <type_traits> + +namespace rttr +{ +namespace detail +{ + ///////////////////////////////////////////////////////////////////////////////////// + + struct member_func_ptr + { + typedef member_func_ptr type; + }; + + struct function_ptr + { + typedef function_ptr type; + }; + + struct member_object_ptr + { + typedef member_object_ptr type; + }; + + struct object_ptr + { + typedef object_ptr type; + }; + + template<typename T> + struct property_type : std::conditional<std::is_member_function_pointer<T>::value, + member_func_ptr, + typename std::conditional<std::is_member_object_pointer<T>::value, + member_object_ptr, + typename std::conditional<is_function_ptr<T>::value || is_std_function<T>::value, + function_ptr, + typename std::conditional<std::is_pointer<T>::value, + object_ptr, + void + >::type + >::type + >::type + >::type + { + }; + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + struct void_member_func + { + typedef void_member_func type; + }; + + struct return_member_func + { + typedef return_member_func type; + }; + + struct void_func + { + typedef void_func type; + }; + + struct return_func + { + typedef return_func type; + }; + + template<typename T> + struct method_type : std::conditional<std::is_member_function_pointer<T>::value, + typename std::conditional<is_void_func<T>::value, + void_member_func, + return_member_func + >::type, + typename std::conditional<is_function_ptr<T>::value || is_std_function<T>::value, + typename std::conditional<is_void_func<T>::value, + void_func, + return_func + >::type, + void + >::type + >::type + { + }; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ACCESSOR_TYPE_H__ diff --git a/src/rttr/detail/argument.h b/src/rttr/detail/argument.h new file mode 100644 index 00000000..3b6d857c --- /dev/null +++ b/src/rttr/detail/argument.h @@ -0,0 +1,104 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ARGUMENT_H__ +#define __RTTR_ARGUMENT_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/misc_type_traits.h" +#include "rttr/type.h" +#include "rttr/variant.h" +#include "rttr/variant_array.h" + +#include <type_traits> +#include <utility> + +namespace rttr +{ +namespace detail +{ +class instance; + +/*! + * This class is used for forwarding the arguments to the function calls. + * + * \remark You should never explicit instantiate this class by yourself. + */ +class argument +{ +public: + argument() : _data(nullptr), _type(impl::get_invalid_type()) {} + argument(const argument& other) : _data(other._data), _type(other._type) {} + argument(variant& var) : _data(var.get_ptr()), _type(var.get_type()) {} + argument(const variant& var) : _data(var.get_ptr()), _type(var.get_type()) {} + argument(variant_array& var) : _data(var.get_ptr()), _type(var.get_type()) {} + argument(const variant_array& var) : _data(var.get_ptr()), _type(var.get_type()) {} + + template<typename T> + argument(const T& data, typename std::enable_if<!std::is_same<argument, T>::value >::type* = 0) + : _data(reinterpret_cast<const void*>(std::addressof(data))), + _type(rttr::type::get<T>()) + { + static_assert(!std::is_same<instance, T>::value, "Don't use the argument class for forwarding an instance!"); + } + + template<typename T> + argument(T& data, typename std::enable_if<!std::is_same<argument, T>::value >::type* = 0) + : _data(reinterpret_cast<const void*>(std::addressof(data))), + _type(rttr::type::get<T>()) + { + static_assert(!std::is_same<instance, T>::value, "Don't use the argument class for forwarding an instance!"); + } + + template<typename T> + bool is_type() const { return rttr::type::get<T>() == _type; } + type get_type() const { return _type; } + void* get_ptr() const { return const_cast<void *>(_data); } + + template<typename T> + T& get_value() const + { + using raw_type = typename std::remove_reference<T>::type; + return (*reinterpret_cast<raw_type*>(const_cast<void *>(_data))); + } + + argument& operator=(const argument& other) + { + _data = other._data; + const_cast<rttr::type&>(_type) = other._type; + return *this; + } + +private: + const void* _data; + const rttr::type _type; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ARGUMENT_H__ diff --git a/src/rttr/detail/array_accessor.h b/src/rttr/detail/array_accessor.h new file mode 100644 index 00000000..48dfbe0d --- /dev/null +++ b/src/rttr/detail/array_accessor.h @@ -0,0 +1,708 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ARRAY_ACCESSOR_H__ +#define __RTTR_ARRAY_ACCESSOR_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/utility.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/array_mapper.h" +#include "rttr/variant.h" +#include <type_traits> +#include <cstddef> + +namespace rttr +{ +class type; +namespace detail +{ +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ArrayType, typename B> +struct get_size_from_array_impl; + +template<typename ArrayType> +struct get_size_from_array_impl<ArrayType, std::true_type> +{ + template<typename... Indices> + static std::size_t get_size(const ArrayType& array, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 0)>::type; + return get_size_from_array_impl<sub_type, go_one_dim_deeper>::get_size(array_mapper<ArrayType>::get_value(array, index), args...); + } +}; + +template<typename ArrayType> +struct get_size_from_array_impl<ArrayType, std::false_type> +{ + static std::size_t get_size(const ArrayType& array) + { + return array_mapper<ArrayType>::get_size(array); + } +}; + + +template<typename ArrayType, typename... Indices> +std::size_t get_size_from_array(const ArrayType& array, Indices... args) +{ + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 0) >::type; + return get_size_from_array_impl<ArrayType, go_one_dim_deeper>::get_size(array, args...); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ArrayType, typename B> +struct set_size_to_array_impl; + +template<typename ArrayType> +struct set_size_to_array_impl<ArrayType, std::true_type> +{ + template<typename... Indices> + static bool set_size(ArrayType& array, std::size_t new_size, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 0)>::type; + return set_size_to_array_impl<sub_type, go_one_dim_deeper>::set_size(array_mapper<ArrayType>::get_value(array, index), new_size, args...); + } +}; + +template<typename ArrayType> +struct set_size_to_array_impl<ArrayType, std::false_type> +{ + static bool set_size(ArrayType& array, std::size_t new_size) + { + return array_mapper<ArrayType>::set_size(array, new_size); + } +}; + + +template<typename ArrayType, typename... Indices> +bool set_size_to_array(ArrayType& array, std::size_t new_size, Indices... args) +{ + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 0) >::type; + return set_size_to_array_impl<ArrayType, go_one_dim_deeper>::set_size(array, new_size, args...); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename Return_Type, typename ArrayType, typename B> +struct get_value_from_array_impl; + +template<typename Return_Type, typename ArrayType> +struct get_value_from_array_impl<Return_Type, ArrayType, std::true_type> +{ + template<typename... Indices> + static const Return_Type& get_value(const ArrayType& array, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using arg_count_valid = typename std::integral_constant<bool, sizeof...(Indices) != 0>::type; + return get_value_from_array_impl<Return_Type, sub_type, arg_count_valid>::get_value(array_mapper<ArrayType>::get_value(array, index), args...); + } +}; + +template<typename Return_Type, typename T> +struct get_value_from_array_impl<Return_Type, T, std::false_type> +{ + template<typename... Indices> + static const Return_Type& get_value(const T& value, Indices... args) + { + return value; + } +}; + +template<typename ArrayType, typename... Indices> +variant get_value_from_array(const ArrayType& array, std::size_t index, Indices... args) +{ + static_assert(rank<ArrayType>::value >= sizeof...(Indices) + 1, "Invalid return type! The number of specified indices are out of range."); + using return_type = typename rank_type<ArrayType, sizeof...(Indices) + 1>::type; + using sub_type = typename array_mapper<ArrayType>::sub_type; + using go_one_dim_deeper = typename std::integral_constant<bool, sizeof...(Indices) != 0>::type; + return get_value_from_array_impl<return_type, sub_type, go_one_dim_deeper>::get_value(array_mapper<ArrayType>::get_value(array, index), args...); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ArrayType, typename B> +struct set_value_to_array_impl; + +template<typename ArrayType> +struct set_value_to_array_impl<ArrayType, std::true_type> +{ + template<typename T1, typename... Indices> + static void set_value(ArrayType& array, const T1& value, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using arg_count_valid = typename std::integral_constant<bool, sizeof...(Indices) != 0>::type; + set_value_to_array_impl<sub_type, arg_count_valid>::set_value(array_mapper<ArrayType>::get_value(array, index), value, args...); + } +}; + +template<typename T> +struct set_value_to_array_impl<T, std::false_type> +{ + static void set_value(T& array, const T& value) + { + array = value; + } +}; + +template<typename T, size_t N> +struct set_value_to_array_impl<T[N], std::false_type> +{ + static void set_value(T (& arr)[N], const T (& value)[N]) + { +#if RTTR_COMPILER == RTTR_COMPILER_MSVC +// MSVC is to dumb to let use use the above code, so we have to develop a workaround +// https://connect.microsoft.com/VisualStudio/feedback/details/884930/strange-ambiguous-compile-error-when-forwarding-multi-dimensional-arrays + copy_array(const_cast< T (&)[N]>(value), arr); +#else + copy_array(value, arr); +#endif + } +}; + +template<typename ArrayType, typename F, typename... Indices> +bool set_value_to_array(ArrayType& array, const F& value, std::size_t index, Indices... args) +{ + // MSVC is to dumb to let us put this in the argument list + static_assert(std::is_same<typename rank_type<ArrayType, sizeof...(Indices) + 1>::type, F>::value, "invalid type!"); + using sub_type = typename array_mapper<ArrayType>::sub_type; + using go_one_dim_deeper = typename std::integral_constant<bool, sizeof...(Indices) != 0>::type; + set_value_to_array_impl<sub_type, go_one_dim_deeper>::set_value(array_mapper<ArrayType>::get_value(array, index), value, args...); + return true; +} + +template<typename T> +bool set_value_to_array(T& arg, const T& value) +{ + set_value_to_array_impl<T, std::false_type>::set_value(arg, value); + return true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + + +template<typename ArrayType, typename B> +struct insert_value_to_array_impl; + +template<typename ArrayType> +struct insert_value_to_array_impl<ArrayType, std::true_type> +{ + template<typename T, typename... Indices> + static bool insert_value(ArrayType& array, const T& value, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using arg_count_valid = typename std::integral_constant<bool, (sizeof...(Indices) > 1)>::type; + return insert_value_to_array_impl<sub_type, arg_count_valid>::insert_value(array_mapper<ArrayType>::get_value(array, index), value, args...); + } +}; + +template<typename ArrayType> +struct insert_value_to_array_impl<ArrayType, std::false_type> +{ + template<typename T> + static bool insert_value(ArrayType& array, const T& value, std::size_t index) + { + return array_mapper<ArrayType>::insert_value(array, value, index); + } +}; + + +template<typename ArrayType, typename F, typename... Indices> +bool insert_value_to_array(ArrayType& array, const F& value, Indices... args) +{ + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 1) >::type; + return insert_value_to_array_impl<ArrayType, go_one_dim_deeper>::insert_value(array, value, args...); +} + +///////////////////////////////////////////////////////////////////////////////////////// + + +template<typename ArrayType, typename B> +struct remove_value_from_array_impl; + +template<typename ArrayType> +struct remove_value_from_array_impl<ArrayType, std::true_type> +{ + template<typename... Indices> + static bool remove_value(ArrayType& array, std::size_t index, Indices... args) + { + using sub_type = typename array_mapper<ArrayType>::sub_type; + using arg_count_valid = typename std::integral_constant<bool, (sizeof...(Indices) > 1) >::type; + return remove_value_from_array_impl<sub_type, arg_count_valid>::remove_value(array_mapper<ArrayType>::get_value(array, index), args...); + } +}; + +template<typename ArrayType> +struct remove_value_from_array_impl<ArrayType, std::false_type> +{ + static bool remove_value(ArrayType& array, std::size_t index) + { + return array_mapper<ArrayType>::remove_value(array, index); + } +}; + + +template<typename ArrayType, typename... Indices> +bool remove_value_from_array(ArrayType& array, Indices... args) +{ + using go_one_dim_deeper = typename std::integral_constant<bool, (sizeof...(Indices) > 1) >::type; + return remove_value_from_array_impl<ArrayType, go_one_dim_deeper>::remove_value(array, args...); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ArrayType, typename A> +struct array_accessor_impl; + +template<typename ArrayType> +struct array_accessor_impl<ArrayType, std::true_type> +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static std::size_t get_size(const ArrayType& obj, Indices... args) + { + return get_size_from_array<ArrayType>(obj, args...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool set_size(ArrayType& obj, std::size_t new_size, Indices... args) + { + return set_size_to_array<ArrayType>(obj, new_size, args...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static variant get_value(const ArrayType& obj, Indices... indices) + { + return get_value_from_array<ArrayType>(obj, indices...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool set_value(ArrayType& obj, argument& arg, Indices... indices) + { + using arg_type = typename rank_type<ArrayType, sizeof...(Indices)>::type; + if (arg.is_type<arg_type>()) + return set_value_to_array<ArrayType>(obj, arg.get_value<arg_type>(), indices...); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool insert_value(ArrayType& obj, argument& arg, Indices... indices) + { + using arg_type = typename rank_type<ArrayType, sizeof...(Indices)>::type; + if (arg.is_type<arg_type>()) + return insert_value_to_array<ArrayType>(obj, arg.get_value<arg_type>(), indices...); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool remove_value(ArrayType& obj, Indices... indices) + { + return remove_value_from_array<ArrayType>(obj, indices...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// +}; + +// index count is bigger then the array rank => invalid call +template<typename ArrayType> +struct array_accessor_impl<ArrayType, std::false_type> +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static std::size_t get_size(const ArrayType& obj, Indices... args) + { + return 0; + } + + template<typename... Indices> + static bool set_size(ArrayType& obj, std::size_t new_size, Indices... args) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static variant get_value(const ArrayType& obj, Indices... indices) + { + return variant(); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool set_value(ArrayType& obj, argument& arg, Indices... indices) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool insert_value(ArrayType& obj, argument& arg, Indices... indices) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool remove_value(ArrayType& obj, Indices... indices) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// +}; + + +template<typename T, typename IndexCount> +struct array_accessor_variadic; + +template<typename ArrayType, std::size_t... N> +struct array_accessor_variadic<ArrayType, index_sequence<N...>> +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + static std::size_t get_size(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant< bool, (sizeof...(N) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::get_size(obj, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_size(ArrayType& obj, std::size_t new_size, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant< bool, (sizeof...(N) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::set_size(obj, new_size, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static variant get_value(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(N) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::get_value(obj, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(N) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::set_value(obj, arg, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool insert_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(N) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::insert_value(obj, arg, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool remove_value(ArrayType& obj, const std::vector<std::size_t>& index_list) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(N) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::remove_value(obj, index_list[N]...); + } + + ///////////////////////////////////////////////////////////////////////////////////////// +}; + +template<typename ArrayType, std::size_t N> +struct array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N>> +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + static std::size_t get_size(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::get_size(obj, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::get_size(obj, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_size(ArrayType& obj, std::size_t new_size, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::set_size(obj, new_size, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::set_size(obj, new_size, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static variant get_value(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::get_value(obj, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::get_value(obj, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::set_value(obj, arg, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::set_value(obj, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool insert_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::insert_value(obj, arg, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::insert_value(obj, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool remove_value(ArrayType& obj, const std::vector<std::size_t>& index_list) + { + if (index_list.size() == N) + return array_accessor_variadic<ArrayType, make_index_sequence<N>>::remove_value(obj, index_list); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::remove_value(obj, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static type get_ranke_type(std::size_t index) + { + if (index == N) + return type::get<typename rank_type<ArrayType, N>::type>(); + else + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, N - 1>>::get_ranke_type(index); + } +}; + +template<typename ArrayType> +struct array_accessor_impl<ArrayType, std::integral_constant<std::size_t, 0>> +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + static std::size_t get_size(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + return array_accessor_impl<ArrayType, std::true_type>::get_size(obj); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_size(ArrayType& obj, std::size_t new_size, const std::vector<std::size_t>& index_list) + { + return array_accessor_impl<ArrayType, std::true_type>::set_size(obj, new_size); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static variant get_value(const ArrayType& obj, const std::vector<std::size_t>& index_list) + { + return variant(); // one index at least needed, otherwise the whole array would be returned + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool set_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + // copy the whole array + return array_accessor_impl<ArrayType, std::true_type>::set_value(obj, arg); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool insert_value(ArrayType& obj, argument& arg, const std::vector<std::size_t>& index_list) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static bool remove_value(ArrayType& obj, const std::vector<std::size_t>& index_list) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static type get_ranke_type(std::size_t index) + { + return type::get<typename rank_type<ArrayType, 0>::type>(); + } + + ///////////////////////////////////////////////////////////////////////////////////////// +}; + +template<typename ArrayType> +struct array_accessor +{ + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static std::size_t get_size(const ArrayType& array, Indices... args) + { + using is_rank_in_range = typename std::integral_constant< bool, (sizeof...(Indices) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::get_size(array, args...); + } + + static std::size_t get_size(const ArrayType& array, const std::vector<std::size_t>& index_list) + { + if (index_list.size() < rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value - 1>>::get_size(array, index_list); + else + return 0; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool set_size(ArrayType& array, std::size_t new_size, Indices... args) + { + using is_rank_in_range = typename std::integral_constant< bool, (sizeof...(Indices) < rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::set_size(array, new_size, args...); + } + + static bool set_size(ArrayType& array, std::size_t new_size, const std::vector<std::size_t>& index_list) + { + if (index_list.size() < rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value - 1>>::set_size(array, new_size, index_list); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static variant get_value(const ArrayType& array, Indices... indices) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(Indices) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::get_value(array, indices...); + } + + static variant get_value(const ArrayType& array, const std::vector<std::size_t>& index_list) + { + if (index_list.size() <= rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value>>::get_value(array, index_list); + else + return variant(); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool set_value(ArrayType& array, argument& arg, Indices... indices) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(Indices) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::set_value(array, arg, indices...); + } + + static bool set_value(ArrayType& array, argument& arg, const std::vector<std::size_t>& index_list) + { + if (index_list.size() <= rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value>>::set_value(array, arg, index_list); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool insert_value(ArrayType& array, argument& arg, Indices... indices) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(Indices) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::insert_value(array, arg, indices...); + } + + static bool insert_value(ArrayType& array, argument& arg, const std::vector<std::size_t>& index_list) + { + if (index_list.size() < rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value - 1>>::insert_value(array, arg, index_list); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + template<typename... Indices> + static bool remove_value(ArrayType& array, Indices... indices) + { + using is_rank_in_range = typename std::integral_constant<bool, (sizeof...(Indices) <= rank<ArrayType>::value) >::type; + return array_accessor_impl<ArrayType, is_rank_in_range>::remove_value(array, indices...); + } + + static bool remove_value(ArrayType& array, const std::vector<std::size_t>& index_list) + { + if (index_list.size() < rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value - 1>>::remove_value(array, index_list); + else + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + static type get_ranke_type(std::size_t index) + { + if (index <= rank<ArrayType>::value) + return array_accessor_impl<ArrayType, std::integral_constant<std::size_t, rank<ArrayType>::value>>::get_ranke_type(index); + else + return ::rttr::impl::get_invalid_type(); + } + + ///////////////////////////////////////////////////////////////////////////////////////// +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ARRAY_ACCESSOR_H__ diff --git a/src/rttr/detail/array_container.h b/src/rttr/detail/array_container.h new file mode 100644 index 00000000..14169a40 --- /dev/null +++ b/src/rttr/detail/array_container.h @@ -0,0 +1,731 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ARRAY_CONTAINER_H__ +#define __RTTR_ARRAY_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/array_container_base.h" +#include "rttr/detail/utility.h" +#include "rttr/detail/array_accessor.h" +#include "rttr/detail/array_mapper.h" + +#include <type_traits> +#include <cstddef> + +namespace rttr +{ + +namespace detail +{ +class argument; +template<typename T, typename Enable = void> +class array_container : public array_container_base +{ + using ArrayType = typename detail::raw_type<T>::type; + public: + array_container(const T& arg) + : _value(arg) + { + + } + + array_container(T&& arg) + : _value(std::move(arg)) + { + + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool is_dynamic() const { return array_mapper<ArrayType>::is_dynamic(); } + std::size_t get_rank() const { return rank<ArrayType>::value; } + type get_rank_type(std::size_t index) const { return array_accessor<ArrayType>::get_ranke_type(index); } + type get_type() const { return type::get<T>(); } + bool is_raw_array() const { return std::is_array<ArrayType>::value; } + + ///////////////////////////////////////////////////////////////////////////////////////// + + std::size_t get_size() const + { + return array_accessor<ArrayType>::get_size(_value); + } + std::size_t get_size(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_size(_value, index_1); + } + std::size_t get_size(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_size(_value, index_1, index_2); + } + std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_size(_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_size(std::size_t new_size) + { + return array_accessor<ArrayType>::set_size(_value, new_size); + } + bool set_size(std::size_t new_size, std::size_t index_1) + { + return array_accessor<ArrayType>::set_size(_value, new_size, index_1); + } + bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::set_size(_value, new_size, index_1, index_2); + } + bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::set_size(_value, new_size, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_value(argument& arg) + { + return array_accessor<ArrayType>::set_value(_value, arg); + } + + bool set_value(argument& arg, std::size_t index_1) + { + return array_accessor<ArrayType>::set_value(_value, arg, index_1); + } + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::set_value(_value, arg, index_1, index_2); + } + + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::set_value(_value, arg, index_1, index_2, index_3); + } + + bool set_value_variadic(argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::set_value(_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + variant get_value(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_value(_value, index_1); + } + + variant get_value(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_value(_value, index_1, index_2); + } + + variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const + { + return array_accessor<ArrayType>::get_value(_value, index_1, index_2, index_3); + } + + variant get_value_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_value(_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool insert_value(detail::argument& arg, std::size_t index_1) + { + return array_accessor<ArrayType>::insert_value(_value, arg, index_1); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::insert_value(_value, arg, index_1, index_2); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::insert_value(_value, arg, index_1, index_2, index_3); + } + + bool insert_value_variadic(detail::argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::insert_value(_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool remove_value(std::size_t index_1) + { + return array_accessor<ArrayType>::remove_value(_value, index_1); + } + + bool remove_value(std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::remove_value(_value, index_1, index_2); + } + + bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::remove_value(_value, index_1, index_2, index_3); + } + bool remove_value_variadic(const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::remove_value(_value, index_list); + } + + array_container_base* clone() const { return new array_container<T>(_value); } + + void* get_ptr() const { return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); } + + private: + T _value; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// T* + +template<typename T> +class array_container<T*> : public array_container_base +{ + using ArrayType = typename detail::raw_type<T>::type; + public: + array_container(T* arg) + : _value(static_cast<ArrayType*>(get_void_ptr(arg))) + { + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool is_dynamic() const { return array_mapper<ArrayType>::is_dynamic(); } + std::size_t get_rank() const { return rank<ArrayType>::value; } + type get_rank_type(std::size_t index) const { return array_accessor<ArrayType>::get_ranke_type(index); } + type get_type() const { return type::get<T*>(); } + bool is_raw_array() const { return std::is_array<ArrayType>::value; } + + ///////////////////////////////////////////////////////////////////////////////////////// + + std::size_t get_size() const + { + return array_accessor<ArrayType>::get_size(*_value); + } + std::size_t get_size(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_size(*_value, index_1); + } + std::size_t get_size(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_size(*_value, index_1, index_2); + } + std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_size(*_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_size(std::size_t new_size) + { + return array_accessor<ArrayType>::set_size(*_value, new_size); + } + bool set_size(std::size_t new_size, std::size_t index_1) + { + return array_accessor<ArrayType>::set_size(*_value, new_size, index_1); + } + bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::set_size(*_value, new_size, index_1, index_2); + } + bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::set_size(*_value, new_size, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_value(argument& arg) + { + return array_accessor<ArrayType>::set_value(*_value, arg); + } + + bool set_value(argument& arg, std::size_t index_1) + { + return array_accessor<ArrayType>::set_value(*_value, arg, index_1); + } + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::set_value(*_value, arg, index_1, index_2); + } + + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::set_value(*_value, arg, index_1, index_2, index_3); + } + + bool set_value_variadic(argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::set_value(*_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + variant get_value(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1); + } + + variant get_value(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1, index_2); + } + + variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1, index_2, index_3); + } + + variant get_value_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_value(*_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool insert_value(detail::argument& arg, std::size_t index_1) + { + return array_accessor<ArrayType>::insert_value(*_value, arg, index_1); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::insert_value(*_value, arg, index_1, index_2); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::insert_value(*_value, arg, index_1, index_2, index_3); + } + + bool insert_value_variadic(detail::argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::insert_value(*_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool remove_value(std::size_t index_1) + { + return array_accessor<ArrayType>::remove_value(*_value, index_1); + } + + bool remove_value(std::size_t index_1, std::size_t index_2) + { + return array_accessor<ArrayType>::remove_value(*_value, index_1, index_2); + } + + bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<ArrayType>::remove_value(*_value, index_1, index_2, index_3); + } + bool remove_value_variadic(const std::vector<std::size_t>& index_list) + { + return array_accessor<ArrayType>::remove_value(*_value, index_list); + } + + array_container_base* clone() const { return new array_container<ArrayType*>(_value); } + + void* get_ptr() const { return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); } + + private: + ArrayType* _value; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// const T* + +template<typename T> +class array_container<const T*> : public array_container_base +{ + using ArrayType = typename detail::raw_type<T>::type; + public: + array_container(const T* arg) + : _value(static_cast<const T*>(get_void_ptr(arg))) + { + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool is_dynamic() const { return array_mapper<ArrayType>::is_dynamic(); } + std::size_t get_rank() const { return rank<ArrayType>::value; } + type get_rank_type(std::size_t index) const { return array_accessor<ArrayType>::get_ranke_type(index); } + type get_type() const { return type::get<const T*>(); } + bool is_raw_array() const { return std::is_array<ArrayType>::value; } + + ///////////////////////////////////////////////////////////////////////////////////////// + + std::size_t get_size() const + { + return array_accessor<ArrayType>::get_size(*_value); + } + std::size_t get_size(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_size(*_value, index_1); + } + std::size_t get_size(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_size(*_value, index_1, index_2); + } + std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_size(*_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_size(std::size_t new_size) + { + return false; + } + bool set_size(std::size_t new_size, std::size_t index_1) + { + return false; + } + bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) + { + return false; + } + bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_value(argument& arg) + { + return false; + } + + bool set_value(argument& arg, std::size_t index_1) + { + return false; + } + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2) + { + return false; + } + + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return false; + } + + bool set_value_variadic(argument& arg, const std::vector<std::size_t>& index_list) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + variant get_value(std::size_t index_1) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1); + } + + variant get_value(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1, index_2); + } + + variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const + { + return array_accessor<ArrayType>::get_value(*_value, index_1, index_2, index_3); + } + + variant get_value_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<ArrayType>::get_value(*_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool insert_value(detail::argument& arg, std::size_t index_1) + { + return false; + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2) + { + return false; + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return false; + } + + bool insert_value_variadic(detail::argument& arg, const std::vector<std::size_t>& index_list) + { + return false; + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool remove_value(std::size_t index_1) + { + return false; + } + + bool remove_value(std::size_t index_1, std::size_t index_2) + { + return false; + } + + bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return false; + } + bool remove_value_variadic(const std::vector<std::size_t>& index_list) + { + return false; + } + + array_container_base* clone() const { return new array_container<const T*>(_value); } + void* get_ptr() const { return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); } + + private: + const T* _value; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +class array_container<T, typename std::enable_if<std::is_array<T>::value>::type> : public array_container_base +{ + using ArrayType = typename detail::raw_type<T>::type; + public: + array_container(const T& arg) + { + #if RTTR_COMPILER == RTTR_COMPILER_MSVC + detail::copy_array(const_cast<std::remove_const<T>::type&>(arg), _value); + #else + detail::copy_array(arg, _value); + #endif + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool is_dynamic() const { return array_mapper<T>::is_dynamic(); } + std::size_t get_rank() const { return rank<T>::value; } + type get_rank_type(std::size_t index) const { return array_accessor<T>::get_ranke_type(index); } + type get_type() const { return type::get<T>(); } + bool is_raw_array() const { return std::is_array<T>::value; } + + ///////////////////////////////////////////////////////////////////////////////////////// + + std::size_t get_size() const + { + return array_accessor<T>::get_size(_value); + } + std::size_t get_size(std::size_t index_1) const + { + return array_accessor<T>::get_size(_value, index_1); + } + std::size_t get_size(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<T>::get_size(_value, index_1, index_2); + } + std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<T>::get_size(_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_size(std::size_t new_size) + { + return array_accessor<T>::set_size(_value, new_size); + } + bool set_size(std::size_t new_size, std::size_t index_1) + { + return array_accessor<T>::set_size(_value, new_size, index_1); + } + bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) + { + return array_accessor<T>::set_size(_value, new_size, index_1, index_2); + } + bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) + { + return array_accessor<T>::set_size(_value, new_size, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool set_value(argument& arg) + { + return array_accessor<T>::set_value(_value, arg); + } + + bool set_value(argument& arg, std::size_t index_1) + { + return array_accessor<T>::set_value(_value, arg, index_1); + } + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<T>::set_value(_value, arg, index_1, index_2); + } + + bool set_value(argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<T>::set_value(_value, arg, index_1, index_2, index_3); + } + + bool set_value_variadic(argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<T>::set_value(_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + variant get_value(std::size_t index_1) const + { + return array_accessor<T>::get_value(_value, index_1); + } + + variant get_value(std::size_t index_1, std::size_t index_2) const + { + return array_accessor<T>::get_value(_value, index_1, index_2); + } + + variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const + { + return array_accessor<T>::get_value(_value, index_1, index_2, index_3); + } + + variant get_value_variadic(const std::vector<std::size_t>& index_list) const + { + return array_accessor<T>::get_value(_value, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool insert_value(detail::argument& arg, std::size_t index_1) + { + return array_accessor<T>::insert_value(_value, arg, index_1); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2) + { + return array_accessor<T>::insert_value(_value, arg, index_1, index_2); + } + + bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<T>::insert_value(_value, arg, index_1, index_2, index_3); + } + + bool insert_value_variadic(detail::argument& arg, const std::vector<std::size_t>& index_list) + { + return array_accessor<T>::insert_value(_value, arg, index_list); + } + + ///////////////////////////////////////////////////////////////////////////////////////// + + bool remove_value(std::size_t index_1) + { + return array_accessor<T>::remove_value(_value, index_1); + } + + bool remove_value(std::size_t index_1, std::size_t index_2) + { + return array_accessor<T>::remove_value(_value, index_1, index_2); + } + + bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) + { + return array_accessor<T>::remove_value(_value, index_1, index_2, index_3); + } + bool remove_value_variadic(const std::vector<std::size_t>& index_list) + { + return array_accessor<T>::remove_value(_value, index_list); + } + + array_container_base* clone() const { return new array_container<T>(_value); } + void* get_ptr() const { return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); } + + private: + T _value; + ///////////////////////////////////////////////////////////////////////////////////////// +}; + +template<typename T, typename F> +array_container_base* create_array_container_impl(const F& value, typename std::enable_if<is_array<T>::value>::type* = 0) +{ + using ArrayType = typename std::remove_cv<typename std::remove_reference<F>::type>::type; + return new array_container<ArrayType>(value); +} + +template<typename T, typename F> +array_container_base* create_array_container_impl(const F& value, typename std::enable_if<!is_array<T>::value>::type* = 0) +{ + return nullptr; +} + +template<typename T, typename F> +array_container_base* create_array_container_moved_impl(F&& value, typename std::enable_if<is_array<T>::value>::type* = 0) +{ + using ArrayType = typename std::remove_cv<typename std::remove_reference<F>::type>::type; + return new array_container<ArrayType>(std::move(value)); +} + +template<typename T, typename F> +array_container_base* create_array_container_moved_impl(F&& value, typename std::enable_if<!is_array<T>::value>::type* = 0) +{ + return nullptr; +} + +template<typename T> +array_container_base* create_array_container(const T& value) +{ + return create_array_container_impl<typename detail::raw_type<T>::type, T>(value); +} + +template<typename T> +array_container_base* create_array_container_moved(T&& value) +{ + return create_array_container_moved_impl<typename detail::raw_type<T>::type, T>(std::move(value)); +} + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ARRAY_CONTAINER_H__ diff --git a/src/rttr/detail/array_container_base.h b/src/rttr/detail/array_container_base.h new file mode 100644 index 00000000..5fd14514 --- /dev/null +++ b/src/rttr/detail/array_container_base.h @@ -0,0 +1,95 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ARRAY_CONTAINER_BASE_H__ +#define __RTTR_ARRAY_CONTAINER_BASE_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <vector> +#include <cstddef> + +namespace rttr +{ +class type; +class variant; + +namespace detail +{ + +class instance; +class argument; + +class RTTR_API array_container_base +{ + public: + virtual ~array_container_base() {}; + virtual bool is_dynamic() const = 0; + virtual std::size_t get_rank() const = 0; + virtual type get_rank_type(std::size_t index) const = 0; + virtual type get_type() const = 0; + virtual bool is_raw_array() const = 0; + + virtual std::size_t get_size() const = 0; + virtual std::size_t get_size(std::size_t index_1) const = 0; + virtual std::size_t get_size(std::size_t index_1, std::size_t index_2) const = 0; + virtual std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const = 0; + + virtual bool set_size(std::size_t new_size) = 0; + virtual bool set_size(std::size_t new_size, std::size_t index_1) = 0; + virtual bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) = 0; + virtual bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) = 0; + + virtual bool set_value(argument& arg) = 0; + virtual bool set_value(argument& arg, std::size_t index_1) = 0; + virtual bool set_value(argument& arg, std::size_t index_1, std::size_t index_2) = 0; + virtual bool set_value(argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) = 0; + virtual bool set_value_variadic(argument& arg, const std::vector<std::size_t>& index_list) = 0; + + virtual variant get_value(std::size_t index_1) const = 0; + virtual variant get_value(std::size_t index_1, std::size_t index_2) const = 0; + virtual variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const = 0; + virtual variant get_value_variadic(const std::vector<std::size_t>& index_list) const = 0; + + virtual bool insert_value(detail::argument& arg, std::size_t index_1) = 0; + virtual bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2) = 0; + virtual bool insert_value(detail::argument& arg, std::size_t index_1, std::size_t index_2, std::size_t index_3) = 0; + virtual bool insert_value_variadic(detail::argument& arg, const std::vector<std::size_t>& index_list) = 0; + + virtual bool remove_value(std::size_t index_1) = 0; + virtual bool remove_value(std::size_t index_1, std::size_t index_2) = 0; + virtual bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) = 0; + virtual bool remove_value_variadic(const std::vector<std::size_t>& index_list) = 0; + + virtual array_container_base* clone() const = 0; + virtual void* get_ptr() const = 0; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ARRAY_CONTAINER_BASE_H__ diff --git a/src/rttr/detail/array_mapper.h b/src/rttr/detail/array_mapper.h new file mode 100644 index 00000000..b2bc9aca --- /dev/null +++ b/src/rttr/detail/array_mapper.h @@ -0,0 +1,248 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ARRAY_MAPPER_H__ +#define __RTTR_ARRAY_MAPPER_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <cstddef> +#include <vector> +#include <list> +#include <array> + +namespace rttr +{ +namespace detail +{ +////////////////////////////////////////////////////////////////////////////////////// +/*! + * \brief + */ +template <typename T> +struct array_mapper +{ + using no_array_type = int; + using raw_type = T; + using sub_type = T; +}; + +////////////////////////////////////////////////////////////////////////////////////// + +template <typename T, std::size_t N> +struct array_mapper<T[N]> +{ + using raw_type = typename array_mapper<T>::raw_type; + using sub_type = T; + + static bool is_dynamic() + { + return false; + } + + static std::size_t get_size(const T (&)[N]) + { + return N; + } + + static bool set_size(T (&)[N], std::size_t) + { + return false; + } + + static const T& get_value(const T (& arr)[N], std::size_t index) + { + return arr[index]; + } + + static T& get_value(T (& arr)[N], std::size_t index) + { + return arr[index]; + } + + static bool insert_value(T (&)[N], const T&, std::size_t) + { + return false; + } + + static bool remove_value(T (&)[N], std::size_t) + { + return false; + } + +}; + +////////////////////////////////////////////////////////////////////////////////////// + +template <typename T, std::size_t N> +struct array_mapper<std::array<T, N> > +{ + using raw_type = typename array_mapper<T>::raw_type; + using sub_type = T; + + static bool is_dynamic() + { + return false; + } + + static std::size_t get_size(const std::array<T, N>&) + { + return N; + } + + static bool set_size(std::array<T, N>&, std::size_t) + { + return false; + } + + static const T& get_value(const std::array<T, N>& arr, std::size_t index) + { + return arr[index]; + } + + static T& get_value(std::array<T, N>& arr, std::size_t index) + { + return arr[index]; + } + + static bool insert_value(std::array<T, N>&, const T&, std::size_t) + { + return false; + } + + static bool remove_value(std::array<T, N>&, std::size_t) + { + return false; + } +}; + +////////////////////////////////////////////////////////////////////////////////////// + + template <typename T> + struct array_mapper<std::vector<T> > + { + using raw_type = typename array_mapper<T>::raw_type; + using sub_type = T; + + static bool is_dynamic() + { + return true; + } + + static std::size_t get_size(const std::vector<T>& arr) + { + return arr.size(); + } + + static bool set_size(std::vector<T>& arr, std::size_t new_size) + { + arr.resize(new_size); + return true; + } + + static const T& get_value(const std::vector<T>& arr, std::size_t index) + { + return arr[index]; + } + + static T& get_value(std::vector<T>& arr, std::size_t index) + { + return arr[index]; + } + + static bool insert_value(std::vector<T>& arr, const T& value, std::size_t index) + { + arr.insert(arr.begin() + index, value); + return true; + } + + static bool remove_value(std::vector<T>& arr, std::size_t index) + { + arr.erase(arr.begin() + index); + return true; + } +}; + +////////////////////////////////////////////////////////////////////////////////////// + + template <typename T> + struct array_mapper<std::list<T> > + { + using raw_type = typename array_mapper<T>::raw_type; + using sub_type = T; + + static bool is_dynamic() + { + return true; + } + + static std::size_t get_size(const std::list<T>& arr) + { + return arr.size(); + } + + static bool set_size(std::list<T>& arr, std::size_t new_size) + { + arr.resize(new_size); + return true; + } + + static const T& get_value(const std::list<T>& arr, std::size_t index) + { + typename std::list<T>::const_iterator it = arr.begin(); + std::advance(it, index); + return *it; + } + + static T& get_value(std::list<T>& arr, std::size_t index) + { + typename std::list<T>::iterator it = arr.begin(); + std::advance(it, index); + return *it; + } + + static bool insert_value(std::list<T>& arr, const T& value, std::size_t index) + { + typename std::list<T>::iterator it = arr.begin(); + std::advance(it, index); + arr.insert(it, value); + return true; + } + + static bool remove_value(std::list<T>& arr, std::size_t index) + { + typename std::list<T>::iterator it = arr.begin(); + std::advance(it, index); + arr.erase(it); + return true; + } +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ARRAY_MAPPER_H__ diff --git a/src/rttr/detail/constructor_container.h b/src/rttr/detail/constructor_container.h new file mode 100644 index 00000000..18bd9269 --- /dev/null +++ b/src/rttr/detail/constructor_container.h @@ -0,0 +1,141 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_CONSTRUCTOR_CONTAINER_H__ +#define __RTTR_CONSTRUCTOR_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/constructor_container_base.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/utility.h" +#include "rttr/variant.h" + +#include <vector> +#include <utility> +#include <type_traits> + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename... Args> +class constructor_container : public constructor_container_base +{ + public: + constructor_container() : constructor_container_base(type::get<typename raw_type<ClassType>::type>()) {} + RTTR_INLINE std::vector<type> get_parameter_types_impl(std::false_type) const { return {}; } + RTTR_INLINE std::vector<type> get_parameter_types_impl(std::true_type) const { return { type::get<Args>()...}; } + std::vector<type> get_parameter_types() const { return get_parameter_types_impl(std::integral_constant<bool, sizeof...(Args) != 0>()); } + + type get_instanciated_type() const { return type::get<ClassType*>(); } + + RTTR_INLINE std::vector<bool> get_is_reference_impl(std::true_type) const { return {std::is_reference<Args>::value...}; } + RTTR_INLINE std::vector<bool> get_is_reference_impl(std::false_type) const { return {}; } + + RTTR_INLINE std::vector<bool> get_is_const_impl(std::true_type) const { return {std::is_const<typename std::remove_reference<Args>::type>::value...}; } + RTTR_INLINE std::vector<bool> get_is_const_impl(std::false_type) const { return {}; } + + std::vector<bool> get_is_reference() const { return get_is_reference_impl(std::integral_constant<bool, sizeof...(Args) != 0>()); } + std::vector<bool> get_is_const() const { return get_is_const_impl(std::integral_constant<bool, sizeof...(Args) != 0>()); } + + template<typename... TArgs> + RTTR_INLINE variant invoke_variadic_extracted(TArgs&... args) const + { + if (check_all_true(args. template is_type<Args>()...)) + return (new ClassType(args. template get_value<Args>()...)); + else + return variant(); + } + + template<typename... TArgs> + RTTR_INLINE variant invoke_impl(std::true_type, TArgs&... args) const + { + return invoke_variadic_extracted(args...); + } + + template<typename... TArgs> + RTTR_INLINE variant invoke_impl(std::false_type, TArgs&... args) const + { + return variant(); + } + + variant invoke() const + { + return invoke_impl(std::integral_constant<bool, 0 == sizeof...(Args)>()); + } + + variant invoke(detail::argument& arg1) const + { + return invoke_impl(std::integral_constant<bool, 1 == sizeof...(Args)>(), arg1); + } + variant invoke(detail::argument& arg1, detail::argument& arg2) const + { + return invoke_impl(std::integral_constant<bool, 2 == sizeof...(Args)>(), arg1, arg2); + } + variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3) const + { + return invoke_impl(std::integral_constant<bool, 3 == sizeof...(Args)>(), arg1, arg2, arg3); + } + variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4) const + { + return invoke_impl(std::integral_constant<bool, 4 == sizeof...(Args)>(), arg1, arg2, arg3, arg4); + } + variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, detail::argument& arg5) const + { + return invoke_impl(std::integral_constant<bool, 5 == sizeof...(Args)>(), arg1, arg2, arg3, arg4, arg5); + } + variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, detail::argument& arg5, detail::argument& arg6) const + { + return invoke_impl(std::integral_constant<bool, 6 == sizeof...(Args)>(), arg1, arg2, arg3, arg4, arg5, arg6); + } + + template<std::size_t ...I> + RTTR_INLINE variant invoke_variadic_impl(std::vector<detail::argument>& args, index_sequence<I...>) const + { + if (args.size() == sizeof...(I)) + return invoke_variadic_extracted(args[I]...); + else + return variant(); + } + + variant invoke_variadic(std::vector<detail::argument>& args) const + { + return invoke_variadic_impl(args, make_index_sequence<sizeof...(Args)>()); + } +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_CONSTRUCTOR_CONTAINER_H__ diff --git a/src/rttr/detail/constructor_container_base.cpp b/src/rttr/detail/constructor_container_base.cpp new file mode 100644 index 00000000..d5a3fc58 --- /dev/null +++ b/src/rttr/detail/constructor_container_base.cpp @@ -0,0 +1,147 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/constructor_container_base.h" + +using namespace std; + +static const char* is_ref_list[] = {"", " &"}; +static const char* is_const_list[] = {"", " const"}; + +namespace rttr +{ +namespace detail +{ +///////////////////////////////////////////////////////////////////////////////////////// + +constructor_container_base::constructor_container_base(type decalaring_type) +: _decalaring_type(decalaring_type) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +constructor_container_base::~constructor_container_base() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type constructor_container_base::get_declaring_type() const +{ + return _decalaring_type; +} +std::vector<bool> constructor_container_base::get_is_reference() const +{ + return{}; +} +std::vector<bool> constructor_container_base::get_is_const() const +{ + return{}; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string constructor_container_base::get_signature() const +{ + auto params = get_parameter_types(); + string result = get_instanciated_type().get_raw_type().get_name() + "( "; + std::size_t index = 0; + auto ref_list = get_is_reference(); + auto const_list = get_is_const(); + for (const auto& type : params) + { + result += type.get_name() + string(is_const_list[const_list[index]]) + string(is_ref_list[ref_list[index]]); + if (index < params.size() - 1) + result += ", "; + + ++index; + } + if (params.empty()) + result += ")"; + else + result += " )"; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke() const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1, detail::argument& arg2) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, + detail::argument& arg5) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant constructor_container_base::invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, + detail::argument& arg5, detail::argument& arg6) const +{ + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/constructor_container_base.h b/src/rttr/detail/constructor_container_base.h new file mode 100644 index 00000000..a254a621 --- /dev/null +++ b/src/rttr/detail/constructor_container_base.h @@ -0,0 +1,85 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_CONSTRUCTOR_CONTAINER_BASE_H__ +#define __RTTR_CONSTRUCTOR_CONTAINER_BASE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" +#include "rttr/type.h" +#include "rttr/detail/metadata_container.h" + +#include <string> +#include <vector> + +namespace rttr +{ + +namespace detail +{ +class argument; + +/*! + * Abstract class for a method. + * + * This is the base class for all methods. + * You can invoke the method. + */ +class RTTR_API constructor_container_base : public metadata_container +{ + public: + constructor_container_base(type decalaring_type); + constructor_container_base(); + + virtual ~constructor_container_base(); + type get_declaring_type() const; + std::string get_signature() const; + virtual type get_instanciated_type() const = 0; + virtual std::vector<type> get_parameter_types() const = 0; + + virtual std::vector<bool> get_is_reference() const; + virtual std::vector<bool> get_is_const() const; + + virtual variant invoke() const; + virtual variant invoke(detail::argument& arg1) const; + virtual variant invoke(detail::argument& arg1, detail::argument& arg2) const; + virtual variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3) const; + virtual variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4) const; + virtual variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, + detail::argument& arg5) const; + virtual variant invoke(detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, + detail::argument& arg5, detail::argument& arg6) const; + + virtual variant invoke_variadic(std::vector<detail::argument>& args) const = 0; + private: + const type _decalaring_type; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_CONSTRUCTOR_CONTAINER_BASE_H__ \ No newline at end of file diff --git a/src/rttr/detail/destructor_container.h b/src/rttr/detail/destructor_container.h new file mode 100644 index 00000000..66410da6 --- /dev/null +++ b/src/rttr/detail/destructor_container.h @@ -0,0 +1,59 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_DESTRUCTOR_CONTAINER_H__ +#define __RTTR_DESTRUCTOR_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/destructor_container_base.h" +#include "rttr/variant.h" + +namespace rttr +{ +namespace detail +{ + +template<typename ClassType> +class destructor_container : public destructor_container_base +{ + public: + type get_destructed_type() const { return type::get<ClassType*>(); } + + void invoke(variant& obj) const + { + if (obj.is_type<ClassType*>()) + { + delete obj.get_value<ClassType*>(); + obj = variant(); + } + } +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_DESTRUCTOR_CONTAINER_H__ diff --git a/src/rttr/detail/destructor_container_base.cpp b/src/rttr/detail/destructor_container_base.cpp new file mode 100644 index 00000000..7c9708cc --- /dev/null +++ b/src/rttr/detail/destructor_container_base.cpp @@ -0,0 +1,44 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/destructor_container_base.h" + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +destructor_container_base::~destructor_container_base() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace RTR diff --git a/src/rttr/detail/destructor_container_base.h b/src/rttr/detail/destructor_container_base.h new file mode 100644 index 00000000..7b35ab04 --- /dev/null +++ b/src/rttr/detail/destructor_container_base.h @@ -0,0 +1,61 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_DESTRUCTORCONTAINERBASE_H__ +#define __RTTR_DESTRUCTORCONTAINERBASE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" +#include "rttr/type.h" + +#include <string> +#include <initializer_list> + +namespace rttr +{ + +namespace detail +{ +/*! + * Abstract class for a method. + * + * This is the base class for all methods. + * You can invoke the method. + */ +class RTTR_API destructor_container_base +{ + public: + virtual ~destructor_container_base(); + + virtual type get_destructed_type() const = 0; + virtual void invoke(variant& obj) const = 0; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_DESTRUCTORCONTAINERBASE_H__ diff --git a/src/rttr/detail/enumeration_container.h b/src/rttr/detail/enumeration_container.h new file mode 100644 index 00000000..072908ce --- /dev/null +++ b/src/rttr/detail/enumeration_container.h @@ -0,0 +1,117 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ENUMERATION_CONTAINER_H__ +#define __RTTR_ENUMERATION_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/enumeration_container_base.h" +#include "rttr/detail/argument.h" +#include "rttr/variant.h" + +#include <utility> +#include <type_traits> + +namespace rttr +{ +namespace detail +{ + +template<typename EnumType> +class enumeration_container : public enumeration_container_base +{ + public: + enumeration_container(const type declaring_type, std::vector< std::pair<std::string, EnumType> > data) + : enumeration_container_base(declaring_type), + _keyToValue(move(data)) + { + static_assert(std::is_enum<EnumType>::value, "No enum type provided, please create an instance of this class only for enum types!"); + } + + type get_type() const { return type::get<EnumType>(); } + type get_underlying_type() const { return type::get<typename std::underlying_type<EnumType>::type>(); } + + std::vector<std::string> get_keys() const + { + std::vector<std::string> result; + for (const auto& item : _keyToValue) + { + result.push_back(item.first); + } + return result; + } + + std::vector<variant> get_values() const + { + std::vector<variant> result; + for (const auto& item : _keyToValue) + { + result.push_back(item.second); + } + return result; + } + + std::string value_to_key(detail::argument& value) const + { + if (!value.is_type<EnumType>() && + !value.is_type<typename std::underlying_type<EnumType>::type>()) + { + return std::string(); + } + + EnumType enum_value = value.get_value<EnumType>(); + for (const auto& item : _keyToValue) + { + if (item.second == enum_value) + return item.first; + } + return std::string(); + } + + variant key_to_value(const std::string& key) const + { + if (key.empty()) + return variant(); + + for (const auto& item : _keyToValue) + { + if (item.first == key) + return item.second; + } + return variant(); + } + + private: + std::vector<std::pair<std::string, EnumType> > _keyToValue; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_CONSTRUCTOR_CONTAINER_H__ diff --git a/src/rttr/detail/enumeration_container_base.cpp b/src/rttr/detail/enumeration_container_base.cpp new file mode 100644 index 00000000..1f4b4c31 --- /dev/null +++ b/src/rttr/detail/enumeration_container_base.cpp @@ -0,0 +1,57 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/enumeration_container_base.h" +#include "rttr/detail/argument.h" + +namespace rttr +{ +namespace detail +{ +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration_container_base::enumeration_container_base(const type declaring_type) +: _declaring_type(declaring_type) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration_container_base::~enumeration_container_base() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type enumeration_container_base::get_declaring_type() const +{ + return _declaring_type; +} + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/enumeration_container_base.h b/src/rttr/detail/enumeration_container_base.h new file mode 100644 index 00000000..e59ba1f1 --- /dev/null +++ b/src/rttr/detail/enumeration_container_base.h @@ -0,0 +1,81 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ENUMERATION_CONTAINER_BASE_H__ +#define __RTTR_ENUMERATION_CONTAINER_BASE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" +#include "rttr/type.h" +#include "rttr/detail/metadata_container.h" + +#include <string> +#include <vector> +#include <initializer_list> + +namespace rttr +{ + +namespace detail +{ +class argument; + +/*! + * Abstract class for a method. + * + * This is the base class for all methods. + * You can invoke the method. + */ +class RTTR_API enumeration_container_base : public metadata_container +{ + public: + enumeration_container_base(const type declaring_type); + virtual ~enumeration_container_base(); + + virtual type get_underlying_type() const = 0; + + virtual type get_type() const = 0; + + virtual std::vector<std::string> get_keys() const = 0; + + virtual std::vector<variant> get_values() const = 0; + + virtual std::string value_to_key(detail::argument& value) const = 0; + + virtual variant key_to_value(const std::string& key) const = 0; + + // Returns the class that declares this property. + type get_declaring_type() const; + + private: + const type _declaring_type; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_ENUMERATION_CONTAINER_BASE_H__ diff --git a/src/rttr/detail/function_traits.h b/src/rttr/detail/function_traits.h new file mode 100644 index 00000000..63039bc2 --- /dev/null +++ b/src/rttr/detail/function_traits.h @@ -0,0 +1,166 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_FUNCTION_TRAITS_H__ +#define __RTTR_FUNCTION_TRAITS_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <type_traits> +#include <functional> +#include <tuple> + +namespace rttr +{ +namespace detail +{ + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T> + struct is_std_function : std::false_type + { + typedef T signature; + }; + + template<typename T> + struct is_std_function<std::function<T>> : std::true_type + { + typedef T signature; + }; + + template<typename T> + struct get_std_function_signature + { + typedef T type; + }; + + template<typename T> + struct get_std_function_signature<std::function<T>> + { + typedef T type; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + template<typename T> + struct is_function_ptr : std::integral_constant<bool, std::is_pointer<T>::value && + std::is_function<typename std::remove_pointer<T>::type>::value> + { + }; + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename... Args> + struct function_args + { + typedef std::tuple<Args...> arg_types; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T> + struct function_traits_func_ptr; + + template<typename R, typename... Args> + struct function_traits_func_ptr<R (*)(Args...)> : function_args<Args...> + { + static const size_t arg_count = sizeof...(Args); + typedef R return_type; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T> + struct function_traits_mem_ptr; + + template<typename R, typename C, typename... Args> + struct function_traits_mem_ptr<R (C::*)(Args...)> : function_args<Args...> + { + static const size_t arg_count = sizeof...(Args); + typedef R return_type; + typedef C class_type; + }; + + template<typename R, typename C, typename... Args> + struct function_traits_mem_ptr<R (C::*)(Args...) const> : function_args<Args...> + { + static const size_t arg_count = sizeof...(Args); + typedef R return_type; + typedef C class_type; + }; + + template<typename R, typename C, typename... Args> + struct function_traits_mem_ptr<R (C::*)(Args...) const volatile> : function_args<Args...> + { + static const size_t arg_count = sizeof...(Args); + typedef R return_type; + typedef C class_type; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T> + struct function_traits : std::conditional<std::is_member_function_pointer<T>::value, + function_traits_mem_ptr<T>, + typename std::conditional<std::is_function<T>::value, + function_traits_func_ptr<typename std::add_pointer<T>::type>, + typename std::conditional<is_std_function<T>::value, + function_traits_func_ptr<typename std::add_pointer<typename get_std_function_signature<T>::type>::type>, + function_traits_func_ptr<T> + >::type + >::type + >::type + { + }; + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T, size_t Index> + struct param_types + { + typedef typename std::tuple_element<Index, typename function_traits<T>::arg_types>::type type; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // use it like e.g: + // param_types<F, 0>::type + + template<typename T> + struct is_void_func : std::conditional<std::is_same<typename function_traits<T>::return_type, void>::value, + std::true_type, + std::false_type + >::type + {}; +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_FUNCTION_TRAITS_H__ diff --git a/src/rttr/detail/instance.h b/src/rttr/detail/instance.h new file mode 100644 index 00000000..fb174417 --- /dev/null +++ b/src/rttr/detail/instance.h @@ -0,0 +1,116 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_INSTANCE_H__ +#define __RTTR_INSTANCE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/misc_type_traits.h" +#include "rttr/type.h" +#include "rttr/variant.h" + +namespace rttr +{ +namespace detail +{ + +class argument; +/*! + * This class is used for forwarding the instance of an object to the function calls. + * + * \remark You should never explicit instantiate this class by yourself. + */ +class instance +{ +public: + instance() : _data(nullptr), _type(impl::get_invalid_type()) {} + + instance(variant& var) + : _data(var.get_raw_ptr()), + _type(var.get_raw_type()) + { + } + + instance(const instance& other) + : _data(other._data), + _type(other._type) + { + } + + instance(instance&& other) + : _data(other._data), + _type(other._type) + { + } + + template<typename T> + instance(const T& data, typename std::enable_if<!std::is_same<instance, T>::value >::type* = 0) + : _data(detail::get_void_ptr(data)), + _type(rttr::type::get<typename raw_type<T>::type>()) + { + static_assert(!std::is_same<argument, T>::value, "Don't use the instance class for forwarding an argument!"); + } + + template<typename T> + instance(T& data, typename std::enable_if<!std::is_same<instance, T>::value >::type* = 0) + : _data(detail::get_void_ptr(data)), + _type(rttr::type::get<typename raw_type<T>::type>()) + { + static_assert(!std::is_same<argument, T>::value, "Don't use the instance class for forwarding an argument!"); + } + + template<typename TargetType> + TargetType* try_convert() const + { + return (static_cast<TargetType*>(type::apply_offset(const_cast<instance*>(this)->_data, _type, type::get<TargetType>()))); + } + + bool is_valid() const { return (_data != nullptr); } + operator bool() const { return (_data != nullptr); } + + type get_type() const { return _type; } + +private: + instance& operator=(const instance& other); + +private: + void* _data; + const rttr::type _type; +}; + +} // end namespace detail + +/*! + * \brief Returns a dummy instance object. + * Use this function when you have a static \ref method or \ref property which you need to invoke. + * + * \return An instance object. + */ +RTTR_INLINE static detail::instance empty_instance() { return detail::instance(); } +} // end namespace rttr + +#endif // __RTTR_INSTANCE_H__ diff --git a/src/rttr/detail/metadata_container.cpp b/src/rttr/detail/metadata_container.cpp new file mode 100644 index 00000000..a53543e3 --- /dev/null +++ b/src/rttr/detail/metadata_container.cpp @@ -0,0 +1,79 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/metadata_container.h" +#include "rttr/variant.h" + +#include <utility> + +using namespace std; + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +variant metadata_container::get_metadata(int key) const +{ + const auto value = _int_data.find(key); + if (value != _int_data.end()) + return value->second; + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant metadata_container::get_metadata(const std::string& key) const +{ + const auto value = _string_data.find(key); + if (value != _string_data.end()) + return value->second; + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void metadata_container::set_metadata(int key, const variant& value) +{ + _int_data.insert(make_pair(key, value)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void metadata_container::set_metadata(const std::string& key, const variant& value) +{ + _string_data.insert(make_pair(key, value)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/metadata_container.h b/src/rttr/detail/metadata_container.h new file mode 100644 index 00000000..8c17ed91 --- /dev/null +++ b/src/rttr/detail/metadata_container.h @@ -0,0 +1,67 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METADATA_CONTAINER_H__ +#define __RTTR_METADATA_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" + +#include <string> +#include <unordered_map> + +namespace rttr +{ +namespace detail +{ + +/*! + * This class gives access to a constructor of a rttr::type. + * Discovers the attributes of a class constructor and provides access to constructor metadata. + * + * Calling constructor::invoke will create an instance of the type. + */ +class RTTR_API metadata_container +{ + public: + variant get_metadata(int key) const; + variant get_metadata(const std::string& key) const; + + void set_metadata(int key, const variant& value); + void set_metadata(const std::string& key, const variant& value); + + private: + typedef std::unordered_map<int, variant> IntKey2ValueMap; + typedef std::unordered_map<std::string, variant> StringKey2ValueMap; + IntKey2ValueMap _int_data; + StringKey2ValueMap _string_data; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_METADATA_CONTAINER_H__ diff --git a/src/rttr/detail/method_accessor.h b/src/rttr/detail/method_accessor.h new file mode 100644 index 00000000..5e3b2171 --- /dev/null +++ b/src/rttr/detail/method_accessor.h @@ -0,0 +1,329 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METHOD_ACCESSOR__ +#define __RTTR_METHOD_ACCESSOR__ + +#include "rttr/detail/function_traits.h" +#include "rttr/detail/utility.h" + +namespace rttr +{ +namespace detail +{ + +template<typename F, typename IndexSequence, typename B> +struct method_accessor_impl; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_impl<F, index_sequence<ArgCount...>, std::true_type> +{ + static std::vector<bool> get_is_reference() + { + return { std::is_reference<typename param_types<F, ArgCount>::type>::value... }; + } + + static std::vector<bool> get_is_const() + { + return { std::is_const<typename std::remove_reference<typename param_types<F, ArgCount>::type>::type>::value... }; + } + + static std::vector<type> get_parameter_types() + { + return { type::get<typename param_types<F, ArgCount>::type>()... }; + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_impl<F, index_sequence<ArgCount...>, std::false_type> +{ + static std::vector<bool> get_is_reference() + { + return std::vector<bool>(); + } + + static std::vector<bool> get_is_const() + { + return std::vector<bool>(); + } + + static std::vector<type> get_parameter_types() + { + return std::vector<type>(); + } +}; + +template<typename F, typename Policy, typename Method_Type, typename IndexSequence, typename ArgCountInRange> +struct method_accessor_invoker; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, default_invoke, void_member_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + typedef typename function_traits<F>::class_type C; + C* ptr = obj.try_convert<C>(); + if (ptr && check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + (ptr->*func_ptr)(args.template get_value<typename param_types<F, ArgCount>::type>()...); + return void_variant; + } + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, default_invoke, void_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func, const instance& obj, const TArgs&...args) + { + if (check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + func(args.template get_value<typename param_types<F, ArgCount>::type>()...); + return void_variant; + } + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, default_invoke, return_member_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + typedef typename function_traits<F>::class_type C; + C* ptr = obj.try_convert<C>(); + if (ptr && check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + return (ptr->*func_ptr)(args.template get_value<typename param_types<F, ArgCount>::type>()...); + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, default_invoke, return_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func, const instance& obj, const TArgs&...args) + { + if (check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + return func(args.template get_value<typename param_types<F, ArgCount>::type>()...); + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, discard_return, return_member_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + typedef typename function_traits<F>::class_type C; + C* ptr = obj.try_convert<C>(); + if (ptr && check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + (ptr->*func_ptr)(args.template get_value<typename param_types<F, ArgCount>::type>()...); + return void_variant; + } + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, discard_return, return_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func, const instance& obj, const TArgs&...args) + { + if (check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + func(args.template get_value<typename param_types<F, ArgCount>::type>()...); + return void_variant; + } + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, return_as_ptr, return_member_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + typedef typename function_traits<F>::class_type C; + C* ptr = obj.try_convert<C>(); + if (ptr && check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + return &(ptr->*func_ptr)(args.template get_value<typename param_types<F, ArgCount>::type>()...); + } + else + return variant(); + } +}; + +template<typename F, std::size_t... ArgCount> +struct method_accessor_invoker<F, return_as_ptr, return_func, index_sequence<ArgCount...>, std::true_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func, const instance& obj, const TArgs&...args) + { + if (check_all_true(args.template is_type<typename param_types<F, ArgCount>::type>()...)) + { + return &func(args.template get_value<typename param_types<F, ArgCount>::type>()...); + } + else + return variant(); + } +}; + +template<typename F, typename Policy, typename MethodType, typename IndexSequence> +struct method_accessor_invoker<F, Policy, MethodType, IndexSequence, std::false_type> +{ + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + return variant(); + } +}; + +template<typename F, typename Policy, typename IndexSequence> +struct method_accessor_variadic; + +template<typename F, typename Policy, std::size_t... ArgCount> +struct method_accessor_variadic<F, Policy, index_sequence<ArgCount...>> +{ + static variant invoke(const F& func_ptr, const instance& obj, std::vector<argument>& arg_list) + { + using method_type = typename detail::method_type<F>::type; + return method_accessor_invoker<F, Policy, method_type, index_sequence<ArgCount...>, std::true_type>::invoke(func_ptr, obj, arg_list[ArgCount]...); + } +}; + +template<typename MethodType> +struct method_accessor_helper_is_static +{ + static bool is_static() { return true; } +}; + +template<> +struct method_accessor_helper_is_static<return_member_func> +{ + static bool is_static() { return false; } +}; + +template<> +struct method_accessor_helper_is_static<void_member_func> +{ + static bool is_static() { return false; } +}; + +template<typename F, typename Policy> +struct method_accessor_helper_return_type +{ + static type get_return_type() { return type::get<typename function_traits<F>::return_type>(); } +}; + +template<typename F> +struct method_accessor_helper_return_type<F, return_as_ptr> +{ + using return_type = typename function_traits<F>::return_type; + static type get_return_type() { return type::get<typename std::remove_reference<return_type>::type*>(); } +}; + +template<typename F> +struct method_accessor_helper_return_type<F, discard_return> +{ + static type get_return_type() { return type::get<void>(); } +}; + +template<typename F, typename Policy> +struct method_accessor +{ + static bool is_static() + { + using method_type = typename detail::method_type<F>::type; + return method_accessor_helper_is_static<method_type>::is_static(); + } + + static type get_return_type() + { + return method_accessor_helper_return_type<F, Policy>::get_return_type(); + } + + static std::vector<bool> get_is_reference() + { + const std::size_t ArgCount = function_traits<F>::arg_count; + using has_arguments = typename std::integral_constant<bool, ArgCount != 0>::type; + return method_accessor_impl<F, make_index_sequence<ArgCount>, has_arguments>::get_is_reference(); + } + + static std::vector<bool> get_is_const() + { + const std::size_t ArgCount = function_traits<F>::arg_count; + using has_arguments = typename std::integral_constant<bool, ArgCount != 0>::type; + return method_accessor_impl<F, make_index_sequence<ArgCount>, has_arguments>::get_is_const(); + } + + static std::vector<type> get_parameter_types() + { + const std::size_t ArgCount = function_traits<F>::arg_count; + using has_arguments = typename std::integral_constant<bool, ArgCount != 0>::type; + return method_accessor_impl<F, make_index_sequence<ArgCount>, has_arguments>::get_parameter_types(); + } + + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, const TArgs&...args) + { + const std::size_t ArgCount = function_traits<F>::arg_count; + using method_type = typename detail::method_type<F>::type; + using arg_count_in_range = typename std::integral_constant<bool, ArgCount == sizeof...(args)>::type; + return method_accessor_invoker<F, Policy, method_type, make_index_sequence<ArgCount>, arg_count_in_range>::invoke(func_ptr, obj, args...); + } + + template<typename... TArgs> + RTTR_FORCE_INLINE static variant invoke(const F& func_ptr, const instance& obj, std::vector<argument>& arg_list) + { + const std::size_t ArgCount = function_traits<F>::arg_count; + if (arg_list.size() == ArgCount) + return method_accessor_variadic<F, Policy, make_index_sequence<ArgCount>>::invoke(func_ptr, obj, arg_list); + else + return variant(); + } +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_METHOD_ACCESSOR__ diff --git a/src/rttr/detail/method_container.h b/src/rttr/detail/method_container.h new file mode 100644 index 00000000..04dbb069 --- /dev/null +++ b/src/rttr/detail/method_container.h @@ -0,0 +1,106 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METHOD_CONTAINER_H__ +#define __RTTR_METHOD_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/method_container_base.h" +#include "rttr/detail/function_traits.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" +#include "rttr/detail/accessor_type.h" +#include "rttr/detail/method_accessor.h" +#include "rttr/variant.h" + + +#include <functional> +#include <string> + +namespace rttr +{ +namespace detail +{ + +template<typename F, typename Policy> +class method_container : public method_container_base +{ + public: + method_container(const std::string& name, const type declaring_type, F func_acc) + : method_container_base(name, declaring_type), + _func_acc(func_acc) + { } + + bool is_static() const { return method_accessor<F, Policy>::is_static(); } + type get_return_type() const { return method_accessor<F, Policy>::get_return_type(); } + std::vector<bool> get_is_reference() const { return method_accessor<F, Policy>::get_is_reference(); } + std::vector<bool> get_is_const() const { return method_accessor<F, Policy>::get_is_const(); } + std::vector<type> get_parameter_types() const { return method_accessor<F, Policy>::get_parameter_types(); } + + variant invoke(detail::instance& object) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object); + } + + variant invoke(detail::instance& object, detail::argument& arg1) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1); + } + variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1, arg2); + } + variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1, arg2, arg3); + } + variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1, arg2, arg3, arg4); + } + variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, detail::argument& arg5) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1, arg2, arg3, arg4, arg5); + } + variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, detail::argument& arg4, detail::argument& arg5, detail::argument& arg6) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, arg1, arg2, arg3, arg4, arg5, arg6); + } + + variant invoke_variadic(detail::instance& object, std::vector<detail::argument>& args) const + { + return method_accessor<F, Policy>::invoke(_func_acc, object, args); + } + + private: + F _func_acc; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_METHOD_CONTAINER_H__ diff --git a/src/rttr/detail/method_container_base.cpp b/src/rttr/detail/method_container_base.cpp new file mode 100644 index 00000000..acc00791 --- /dev/null +++ b/src/rttr/detail/method_container_base.cpp @@ -0,0 +1,99 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/method_container_base.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" + +using namespace std; + +static const char* is_ref_list[] = {"", " &"}; +static const char* is_const_list[] = {"", " const"}; + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +method_container_base::method_container_base(const std::string& name, const type decalaring_type) +: _name(name), + _decalaring_type(decalaring_type) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method_container_base::~method_container_base() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string method_container_base::get_name() const +{ + return _name; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type method_container_base::get_declaring_type() const +{ + return _decalaring_type; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string method_container_base::get_signature() const +{ + auto params = get_parameter_types(); + string result = _name + "( "; + std::size_t index = 0; + auto ref_list = get_is_reference(); + auto const_list = get_is_const(); + for (const auto& type : params) + { + result += type.get_name() + string(is_const_list[const_list[index]]) + string(is_ref_list[ref_list[index]]); + if (index < params.size() - 1) + result += ", "; + + ++index; + } + if (params.empty()) + result += ")"; + else + result += " )"; + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/method_container_base.h b/src/rttr/detail/method_container_base.h new file mode 100644 index 00000000..cdfa94c8 --- /dev/null +++ b/src/rttr/detail/method_container_base.h @@ -0,0 +1,91 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METHOD_CONTAINER_BASE_H__ +#define __RTTR_METHOD_CONTAINER_BASE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" +#include "rttr/detail/metadata_container.h" + +#include <string> +#include <vector> + +namespace rttr +{ +class type; + +namespace detail +{ +class argument; +class instance; + +/*! + * Abstract class for a method. + * + * This is the base class for all methods. + * You can invoke the method via method_container_base::invoke. + */ +class RTTR_API method_container_base : public metadata_container +{ + public: + method_container_base(const std::string& name, const type decalaring_type); + + virtual ~method_container_base(); + + std::string get_name() const; + type get_declaring_type() const; + std::string get_signature() const; + + virtual type get_return_type() const = 0; + virtual bool is_static() const = 0; + virtual std::vector<type> get_parameter_types() const = 0; + virtual std::vector<bool> get_is_reference() const = 0; + virtual std::vector<bool> get_is_const() const = 0; + + virtual variant invoke(detail::instance& object) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, + detail::argument& arg4) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, + detail::argument& arg4, detail::argument& arg5) const = 0; + virtual variant invoke(detail::instance& object, detail::argument& arg1, detail::argument& arg2, detail::argument& arg3, + detail::argument& arg4, detail::argument& arg5, detail::argument& arg6) const = 0; + + virtual variant invoke_variadic(detail::instance& object, std::vector<detail::argument>& args) const = 0; + + private: + const std::string _name; + const type _decalaring_type; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_METHOD_CONTAINER_BASE_H__ diff --git a/src/rttr/detail/misc_type_traits.h b/src/rttr/detail/misc_type_traits.h new file mode 100644 index 00000000..baca5ef8 --- /dev/null +++ b/src/rttr/detail/misc_type_traits.h @@ -0,0 +1,396 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_MISC_TYPE_TRAITS_H__ +#define __RTTR_MISC_TYPE_TRAITS_H__ + +#include "rttr/base/core_prerequisites.h" + +#include "rttr/detail/array_mapper.h" + +#include <type_traits> + +namespace rttr +{ + +class type; + +namespace detail +{ + struct derived_info; + + template<class T> + struct raw_type { typedef T type; }; + + template<class T> struct raw_type<const T> { typedef typename raw_type<T>::type type; }; + + template<class T> struct raw_type<T*> { typedef typename raw_type<T>::type type; }; + template<class T> struct raw_type<T* const> { typedef typename raw_type<T>::type type; }; + template<class T> struct raw_type<T* volatile> { typedef typename raw_type<T>::type type; }; + + template<class T> struct raw_type<T&> { typedef typename raw_type<T>::type type; }; + template<class T, std::size_t N> + struct raw_type<const T[N]> { typedef typename raw_type<T[N]>::type type; }; + + + ///////////////////////////////////////////////////////////////////////////////////////// + + template <bool... b> struct static_all_of; + //specialization for type true, go continue recurse if the first argument is true + template <bool... tail> + struct static_all_of<true, tail...> : static_all_of<tail...> {}; + // end recursion if first argument is false + template <bool... tail> + struct static_all_of<false, tail...> : std::false_type {}; + + // finish when no argument are left + template <> struct static_all_of<> : std::true_type {}; + + // use it like e.g.: + // static_all_of<std::is_class<ClassType>::value...>::value + + ///////////////////////////////////////////////////////////////////////////////////////// + + template <bool... b> struct static_any_of; + + template <bool... tail> + struct static_any_of<true, tail...> : std::true_type {}; + + template <bool... tail> + struct static_any_of<false, tail...> : static_any_of<tail...> {}; + + // finish when no argument are left + template <> struct static_any_of<> : std::false_type {}; + + ///////////////////////////////////////////////////////////////////////////////////////// + /*! + * Determine if the given type \a T has the method + * 'type get_type() const' declared. + */ + template <typename T> + class has_get_type_func_impl + { + typedef char YesType[1]; + typedef char NoType[2]; + + template <typename U, rttr::type (U::*)() const> + class check { }; + + template <typename C> + static YesType& f(check<C, &C::get_type>*); + + template <typename C> + static NoType& f(...); + + public: + static const bool value = (sizeof(f<typename raw_type<T>::type>(0)) == sizeof(YesType)); + }; + + /*! + * If T has a member function 'type get_type() const;' then inherits from true_type, otherwise inherits from false_type. + */ + template<class T, typename Enable = void> + struct has_get_type_func : std::false_type + {}; + + template<class T> + struct has_get_type_func<T, typename std::enable_if<has_get_type_func_impl<T>::value>::type > : std::true_type + {}; + + ///////////////////////////////////////////////////////////////////////////////// + + /*! + * Determine if the given type \a T has the method + * 'type get_type() const' declared. + */ + template <typename T> + class has_get_ptr_func_impl + { + typedef char YesType[1]; + typedef char NoType[2]; + + template <typename U, void* (U::*)()> + class check { }; + + template <typename C> + static YesType& f(check<C, &C::get_ptr>*); + + template <typename C> + static NoType& f(...); + + public: + static const bool value = (sizeof(f<typename raw_type<T>::type>(0)) == sizeof(YesType)); + }; + + /*! + * If T has a member function 'type get_type() const;' then inherits from true_type, otherwise inherits from false_type. + */ + template<class T, typename Enable = void> + struct has_get_ptr_func : std::false_type + {}; + + template<class T> + struct has_get_ptr_func<T, typename std::enable_if<has_get_ptr_func_impl<T>::value>::type > : std::true_type + {}; + + ///////////////////////////////////////////////////////////////////////////////// + + /*! + * Determine if the given type \a T has the method + * 'type get_type() const' declared. + */ + template <typename T> + class has_get_derived_info_func_impl + { + typedef char YesType[1]; + typedef char NoType[2]; + + template <typename U, derived_info (U::*)()> + class check { }; + + template <typename C> + static YesType& f(check<C, &C::get_derived_info>*); + + template <typename C> + static NoType& f(...); + + public: + static const bool value = (sizeof(f<typename raw_type<T>::type>(0)) == sizeof(YesType)); + }; + + /*! + * If T has a member function 'type get_type() const;' then inherits from true_type, otherwise inherits from false_type. + */ + template<class T, typename Enable = void> + struct has_get_derived_info_func : std::false_type + {}; + + template<class T> + struct has_get_derived_info_func<T, typename std::enable_if<has_get_derived_info_func_impl<T>::value>::type > : std::true_type + {}; + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T> + struct get_ptr_impl + { + static RTTR_INLINE void* get(T& data) + { + return const_cast<void*>(reinterpret_cast<const void*>(&data)); + } + }; + + template<typename T> + struct get_ptr_impl<T*> + { + static RTTR_INLINE void* get(T* data) + { + return get_ptr_impl<T>::get(*data); + } + }; + + template<> + struct get_ptr_impl<void*> + { + static RTTR_INLINE void* get(void* data) + { + return data; + } + }; + + template<> + struct get_ptr_impl<const void*> + { + static RTTR_INLINE void* get(const void* data) + { + return const_cast<void*>(data); + } + }; + + template<typename T> + static RTTR_INLINE void* get_void_ptr(T* data) + { + return get_ptr_impl<T*>::get(data); + } + + template<typename T> + static RTTR_INLINE void* get_void_ptr(T& data) + { + return get_ptr_impl<T>::get(data); + } + + ///////////////////////////////////////////////////////////////////////////////////// + + template<typename T, typename... Types> + struct contains : static_any_of<std::is_same<T, Types>::value...> + { + }; + + template<typename T, typename... Types, template<class...> class TContainer> + struct contains<T, TContainer<Types...>> : contains<T, Types...> + { + }; + + ///////////////////////////////////////////////////////////////////////////////////// + + template <typename T> + struct is_array_impl + { + typedef char YesType[1]; + typedef char NoType[2]; + + template <typename U> static NoType& check(typename U::no_array_type*); + template <typename U> static YesType& check(...); + + + static const bool value = (sizeof(check<array_mapper<T> >(0)) == sizeof(YesType)); + }; + + template<class T> + struct is_array : std::conditional<is_array_impl<T>::value, + std::true_type, + std::false_type>::type + {}; + + ///////////////////////////////////////////////////////////////////////////////////// + // rank_type<T, size_t>::type + // + // rank_type<int[2][10][4], 0>::type => int[2][10][4] + // rank_type<int[2][10][4], 1>::type => int[10][4] + // rank_type<int[2][10][4], 2>::type => int[4] + // works of course with all other class, which has an array_mapper specialization + + template <typename... T> + struct concat_array_types; + + + template <template <typename ...> class List, typename ...Types, typename T> + struct concat_array_types<List<Types...>, T, std::true_type> + { + using type = List<Types...>; + }; + + template <template <typename... > class List, typename... Types, typename T> + struct concat_array_types<List<Types...>, T, std::false_type> + { + using sub_type = typename array_mapper<T>::sub_type; + using type = typename concat_array_types< List<Types..., T>, sub_type, typename std::is_same<T, sub_type>::type>::type; + }; + + template<typename T> + struct array_rank_type_list + { + using sub_type = typename array_mapper<T>::sub_type; + using types = typename concat_array_types< std::tuple<>, T, typename std::is_same<T, sub_type>::type>::type; + }; + + template<typename T, size_t N> + struct rank_type + { + using type = typename std::tuple_element<N, typename array_rank_type_list<T>::types>::type; + }; + + ///////////////////////////////////////////////////////////////////////////////////// + // rank<T>::value + // + // rank<int[2][10][4]>::value => 3 + // rank<std::vector<std::vector<int>>>::value => 2 + template <typename... T> + struct rank_impl + { + using type = typename std::integral_constant<std::size_t, 0>::type; + }; + + template <template <typename... > class List, typename... Types> + struct rank_impl<List<Types...>> + { + using type = typename std::integral_constant<std::size_t, sizeof...(Types) - 1>::type; + }; + + template<typename T> + using rank = typename rank_impl< typename detail::array_rank_type_list<T>::types >::type; + + ///////////////////////////////////////////////////////////////////////////////////// + // pointer_count<T>::value Returns the number of pointers for a type + // e.g. pointer_count<char**>::value => 2 + // pointer_count<char*>::value => 1 + // pointer_count<char>::value => 0 + template<typename T> + struct pointer_count_impl + { + static const std::size_t size = 0; + }; + + + template<typename T> + struct pointer_count_impl<T*> + { + static const std::size_t size = pointer_count_impl<T>::size + 1; + }; + + //template<typename T> + //using pointer_count = std::integral_constant<std::size_t, pointer_count_impl<T>::size>; + template<typename T> + struct pointer_count : std::integral_constant<std::size_t, pointer_count_impl<T>::size> + {}; + + ///////////////////////////////////////////////////////////////////////////////////// + // is_char_array<T>::value Returns true if the given typ is a char array + // e.g. is_char_array<char[10]>::value => true + // is_char_array<int[10]>::value => false + // is_char_array<char>::value => false + template<typename T> + struct char_array_impl : std::false_type + { + }; + + + template<std::size_t N> + struct char_array_impl<char[N]> : std::true_type + { + }; + + template<typename T> + using is_char_array = char_array_impl<T>; + + template<typename T> + struct is_one_dim_char_array : std::integral_constant<bool, is_char_array<T>::value && (std::rank<T>::value == 1)> + {}; + + template<typename T> + struct is_array_and_not_one_dim_char_array : std::integral_constant<bool, std::is_array<T>::value && !is_one_dim_char_array<T>::value> + { + + }; + + ///////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail + +} // end namespace rttr + +#endif // __RTTR_MISC_TYPE_TRAITS_H__ diff --git a/src/rttr/detail/property_accessor.h b/src/rttr/detail/property_accessor.h new file mode 100644 index 00000000..55606297 --- /dev/null +++ b/src/rttr/detail/property_accessor.h @@ -0,0 +1,79 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_ACCESSOR__ +#define __RTTR_PROPERTY_ACCESSOR__ + +namespace rttr +{ +namespace detail +{ + +template<typename T> +struct property_accessor +{ + static bool set_value(T& prop, argument& arg) + { + prop = arg.get_value<T>(); + return true; + } +}; + +template<typename T, std::size_t N> +struct property_accessor<T[N]> +{ + static bool set_value(T (& prop)[N], argument& arg) + { + copy_array(arg.get_value<T[N]>(), prop); + return true; + } +}; + +template<typename T> +struct property_accessor<T*> +{ + static bool set_value(T* prop, argument& arg) + { + *prop = *arg.get_value<T*>(); + return true; + } +}; + +template<typename T, std::size_t N> +struct property_accessor<T(*)[N]> +{ + static bool set_value(T (* prop)[N], argument& arg) + { + copy_array(*arg.get_value<T(*)[N]>(), *prop); + return true; + } +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_PROPERTY_ACCESSOR_ diff --git a/src/rttr/detail/property_container.h b/src/rttr/detail/property_container.h new file mode 100644 index 00000000..b1384f31 --- /dev/null +++ b/src/rttr/detail/property_container.h @@ -0,0 +1,60 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_H__ +#define __RTTR_PROPERTY_CONTAINER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/function_traits.h" +#include "rttr/detail/property_container_base.h" +#include "rttr/detail/instance.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/accessor_type.h" +#include "rttr/policy.h" +#include "rttr/detail/array_mapper.h" +#include "rttr/detail/utility.h" +#include "rttr/detail/property_accessor.h" + +#include <functional> + +namespace rttr +{ +namespace detail +{ + +template<typename Accessor_Type, typename Getter, typename Setter, typename Get_Policy, typename Set_Policy> +class property_container; + +#include "rttr/detail/property_container_member_func.h" +#include "rttr/detail/property_container_func.h" +#include "rttr/detail/property_container_member_object.h" +#include "rttr/detail/property_container_object.h" + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_PROPERTY_CONTAINER_H__ diff --git a/src/rttr/detail/property_container_base.cpp b/src/rttr/detail/property_container_base.cpp new file mode 100644 index 00000000..5bd1ea83 --- /dev/null +++ b/src/rttr/detail/property_container_base.cpp @@ -0,0 +1,66 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/property_container_base.h" + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +property_container_base::property_container_base(const std::string& name, const type decalaring_type) +: _name(name), + _decalaring_type(decalaring_type) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +property_container_base::~property_container_base() +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string property_container_base::get_name() const +{ + return _name; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type property_container_base::get_declaring_type() const +{ + return _decalaring_type; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/property_container_base.h b/src/rttr/detail/property_container_base.h new file mode 100644 index 00000000..1702b07d --- /dev/null +++ b/src/rttr/detail/property_container_base.h @@ -0,0 +1,89 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_BASE_H__ +#define __RTTR_PROPERTY_CONTAINER_BASE_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/metadata_container.h" +#include "rttr/type.h" + +#include <string> + +namespace rttr +{ + +namespace detail +{ +class instance; +class argument; +/*! + * Abstract class for an instance of a Property. + * + * This is the base class for all properties of the system. + * It provide the basic mechanism for getting all meta data of a property, + * but it also define a general interface to set/get properties via string: toString and fromString. + */ +class RTTR_API property_container_base : public metadata_container +{ + public: + property_container_base(const std::string& name, const type decalaring_type); + + virtual ~property_container_base(); + + // returns the name of this property. + std::string get_name() const; + + //! Returns true whether this is a constant property, otherwise false. + virtual bool is_readonly() const = 0; + + //! Returns true whether this is a static property, otherwise false. + virtual bool is_static() const = 0; + + //! Returns the type of the underlying property. + virtual type get_type() const = 0; + + //! Returns the class that declares this property. + type get_declaring_type() const; + + //! Returns true when the underlying property is an array type. + virtual bool is_array() const = 0; + + //! Sets this property of the given instance \p instance to the value of the argument \p argument. + virtual bool set_value(detail::instance& object, detail::argument& arg) const = 0; + + //! Returns the value of this property from the given instance \p instance. + virtual variant get_value(detail::instance& object) const = 0; + private: + const std::string _name; + const type _decalaring_type; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_PROPERTY_CONTAINER_BASE_H__ diff --git a/src/rttr/detail/property_container_func.h b/src/rttr/detail/property_container_func.h new file mode 100644 index 00000000..e4882064 --- /dev/null +++ b/src/rttr/detail/property_container_func.h @@ -0,0 +1,202 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_FUNC_H__ +#define __RTTR_PROPERTY_CONTAINER_FUNC_H__ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global function getter/setter - function pointer + +template<typename Getter, typename Setter> +class property_container<function_ptr, Getter, Setter, return_as_copy, set_value> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using arg_type = typename param_types<Setter, 0>::type; + + public: + property_container(const std::string& name, const type declaring_type, Getter getter, Setter setter) + : property_container_base(name, declaring_type), + _getter(getter), + _setter(setter) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-function without arguments."); + static_assert(function_traits<Setter>::arg_count == 1, "Invalid number of argument, please provide a setter-function with exactly one argument."); + static_assert(std::is_same<return_type, arg_type>::value, "Please provide the same signature for getter and setter!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return true; } + type get_type() const { return type::get<return_type>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + if (arg.is_type<arg_type>()) + { + _setter(arg.get_value<arg_type>()); + return true; + } + return false; + } + + variant get_value(detail::instance& object) const + { + return variant(_getter()); + } + + private: + Getter _getter; + Setter _setter; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global function getter + +template<typename Getter> +class property_container<function_ptr, Getter, void, return_as_copy, read_only> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + + public: + property_container(const std::string& name, const type declaring_type, Getter func) + : property_container_base(name, declaring_type), + _accessor(func) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-function without arguments."); + } + + bool is_readonly() const { return true; } + bool is_static() const { return true; } + type get_type() const { return type::get<return_type>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + return (variant(_accessor())); + } + + private: + Getter _accessor; +}; + + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global function getter/setter + +template<typename Getter, typename Setter> +class property_container<function_ptr, Getter, Setter, return_as_ptr, set_as_ptr> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using arg_type = typename param_types<Setter, 0>::type; + + public: + property_container(const std::string& name, const type declaring_type, Getter getter, Setter setter) + : property_container_base(name, declaring_type), + _getter(getter), + _setter(setter) + { + static_assert(std::is_reference<return_type>::value, "Please provide a getter-function with a reference as return value!"); + static_assert(std::is_reference<arg_type>::value, "Please provide a setter-function with a reference as argument!"); + + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-function without arguments."); + static_assert(function_traits<Setter>::arg_count == 1, "Invalid number of argument, please provide a setter-function with exactly one argument."); + + static_assert(std::is_same<return_type, arg_type>::value, "Please provide the same signature for getter and setter!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return true; } + type get_type() const { return type::get<typename std::remove_reference<return_type>::type*>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + using arg_type = typename std::remove_reference<arg_type>::type; + if (arg.is_type<arg_type*>()) + { + _setter(*arg.get_value<arg_type*>()); + return true; + } + return false; + } + + variant get_value(detail::instance& object) const + { + return variant(&(_getter())); + } + + private: + Getter _getter; + Setter _setter; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global function getter + +template<typename Getter> +class property_container<function_ptr, Getter, void, return_as_ptr, read_only> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + public: + property_container(const std::string& name, const type declaring_type, Getter func) + : property_container_base(name, declaring_type), + _accessor(func) + { + static_assert(std::is_reference<return_type>::value, "Please provide a function with a reference as return value!"); + } + + bool is_readonly() const { return true; } + bool is_static() const { return true; } + type get_type() const { return type::get<typename std::add_const<typename std::remove_reference<return_type>::type>::type*>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + return (variant(const_cast<const typename std::remove_reference<return_type>::type*>(&(_accessor())))); + } + + private: + Getter _accessor; +}; + + +#endif // __RTTR_PROPERTY_CONTAINER_FUNC_H__ diff --git a/src/rttr/detail/property_container_member_func.h b/src/rttr/detail/property_container_member_func.h new file mode 100644 index 00000000..5105709c --- /dev/null +++ b/src/rttr/detail/property_container_member_func.h @@ -0,0 +1,220 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_MEMBER_FUNC_H__ +#define __RTTR_PROPERTY_CONTAINER_MEMBER_FUNC_H__ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// Getter/Setter - pointer to member function + +template<typename Getter, typename Setter> +class property_container<member_func_ptr, Getter, Setter, return_as_copy, set_value> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using arg_type = typename param_types<Setter, 0>::type; + using class_type = typename function_traits<Getter>::class_type; + + public: + property_container(const std::string& name, const type declaring_type, Getter get, Setter set) + : property_container_base(name, declaring_type), + _getter(get), + _setter(set) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-member-function without arguments."); + static_assert(function_traits<Setter>::arg_count == 1, "Invalid number of argument, please provide a setter-member-function with exactly one argument."); + static_assert(std::is_same<return_type, arg_type>::value, "Please provide the same signature for getter and setter!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return false; } + type get_type() const { return type::get<return_type>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + class_type* ptr = object.try_convert<class_type>(); + if (ptr && arg.is_type<arg_type>() ) + { + (ptr->*_setter)(arg.get_value<arg_type>()); + return true; + } + return false; + } + + variant get_value(detail::instance& object) const + { + if (class_type* ptr = object.try_convert<class_type>()) + return variant((ptr->*_getter)()); + else + return variant(); + } + + private: + Getter _getter; + Setter _setter; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// Getter - pointer to member function + +template<typename Getter> +class property_container<member_func_ptr, Getter, void, return_as_copy, read_only> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using class_type = typename function_traits<Getter>::class_type; + + public: + property_container(const std::string& name, const type declaring_type, Getter get) + : property_container_base(name, declaring_type), + _getter(get) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-member-function without arguments."); + } + + bool is_readonly() const { return false; } + bool is_static() const { return false; } + type get_type() const { return type::get<return_type>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + if (class_type* ptr = object.try_convert<class_type>()) + return variant((ptr->*_getter)()); + else + return variant(); + } + + private: + Getter _getter; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// Policy return_as_ptr +///////////////////////////////////////////////////////////////////////////////////////// + +// Getter/Setter pointer to member function +template<typename Getter, typename Setter> +class property_container<member_func_ptr, Getter, Setter, return_as_ptr, set_as_ptr> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using arg_type = typename param_types<Setter, 0>::type; + using class_type = typename function_traits<Getter>::class_type; + + public: + property_container(const std::string& name, const type declaring_type, Getter get, Setter set) + : property_container_base(name, declaring_type), + _getter(get), + _setter(set) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-member-function without arguments."); + static_assert(function_traits<Setter>::arg_count == 1, "Invalid number of argument, please provide a setter-member-function with exactly one argument."); + static_assert(std::is_same<return_type, arg_type>::value, "Please provide the same signature for getter and setter!"); + + static_assert(std::is_reference<return_type>::value, "Please provide a getter-member-function with a reference as return value!"); + static_assert(std::is_reference<arg_type>::value, "Please provide a setter-member-function with a reference as return value!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return false; } + type get_type() const { return type::get<typename std::remove_reference<return_type>::type*>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + using arg_type = typename std::remove_reference<arg_type>::type; + class_type* ptr = object.try_convert<class_type>(); + if (ptr && arg.is_type<arg_type*>()) + { + (ptr->*_setter)(*arg.get_value<arg_type*>()); + return true; + } + return false; + } + + variant get_value(detail::instance& object) const + { + if (class_type* ptr = object.try_convert<class_type>()) + return variant(&(ptr->*_getter)()); + else + return variant(); + } + + private: + Getter _getter; + Setter _setter; + +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// Getter - pointer to member function + +template<typename Getter> +class property_container<member_func_ptr, Getter, void, return_as_ptr, read_only> : public property_container_base +{ + using return_type = typename function_traits<Getter>::return_type; + using class_type = typename function_traits<Getter>::class_type; + + public: + property_container(const std::string& name, const type declaring_type, Getter get) + : property_container_base(name, declaring_type), + _getter(get) + { + static_assert(function_traits<Getter>::arg_count == 0, "Invalid number of argument, please provide a getter-member-function without arguments."); + static_assert(std::is_reference<return_type>::value, "Please provide a getter-member-function with a reference as return value!"); + } + + bool is_readonly() const { return true; } + bool is_static() const { return false; } + type get_type() const { return type::get<typename std::remove_reference<return_type>::type*>(); } + bool is_array() const { return detail::is_array<return_type>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + if (class_type* ptr = object.try_convert<class_type>()) + return variant(&(ptr->*_getter)()); + else + return variant(); + } + + private: + Getter _getter; +}; + +#endif // __RTTR_PROPERTY_CONTAINER_MEMBER_FUNC_H__ diff --git a/src/rttr/detail/property_container_member_object.h b/src/rttr/detail/property_container_member_object.h new file mode 100644 index 00000000..47fd54cf --- /dev/null +++ b/src/rttr/detail/property_container_member_object.h @@ -0,0 +1,192 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_MEMBER_OBJECT_H__ +#define __RTTR_PROPERTY_CONTAINER_MEMBER_OBJECT_H__ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// pointer to member - read write + +template<typename C, typename A> +class property_container<member_object_ptr, A(C::*), void, return_as_copy, set_value> : public property_container_base +{ + typedef A (C::*accessor); + public: + property_container(const std::string& name, const type declaring_type, accessor acc) + : property_container_base(name, declaring_type), _acc(acc) + { + } + + bool is_readonly() const { return false; } + bool is_static() const { return false; } + type get_type() const { return type::get<A>(); } + bool is_array() const { return detail::is_array<A>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + C* ptr = object.try_convert<C>(); + if (ptr && arg.is_type<A>()) + return property_accessor<A>::set_value((ptr->*_acc), arg); + else + return false; + } + + variant get_value(detail::instance& object) const + { + if (C* ptr = object.try_convert<C>()) + return variant((ptr->*_acc)); + else + return variant(); + } + + private: + accessor _acc; +}; + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// pointer to member - read only (because of std::false_type) + +template<typename C, typename A> +class property_container<member_object_ptr, A(C::*), void, return_as_copy, read_only> : public property_container_base +{ + typedef A (C::*accessor); + public: + property_container(const std::string& name, const type declaring_type, accessor acc) + : property_container_base(name, declaring_type), + _acc(acc) + { + } + + bool is_readonly() const { return true; } + bool is_static() const { return false; } + type get_type() const { return type::get<A>(); } + bool is_array() const { return detail::is_array<A>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + if (C* ptr = object.try_convert<C>()) + return variant((ptr->*_acc)); + else + return variant(); + } + + private: + accessor _acc; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// pointer to member - read write + +template<typename C, typename A> +class property_container<member_object_ptr, A(C::*), void, return_as_ptr, set_as_ptr> : public property_container_base +{ + typedef A (C::*accessor); + public: + property_container(const std::string& name, const type declaring_type, accessor acc) + : property_container_base(name, declaring_type), _acc(acc) + { + static_assert(!std::is_pointer<A>::value, "The given type is already a pointer type!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return false; } + type get_type() const { return type::get<A*>(); } + bool is_array() const { return detail::is_array<A>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + C* ptr = object.try_convert<C>(); + if (ptr && arg.is_type<A*>()) + { + return property_accessor<A*>::set_value(&(ptr->*_acc), arg); + } + else + { + return false; + } + } + + variant get_value(detail::instance& object) const + { + if (C* ptr = object.try_convert<C>()) + return variant(&(ptr->*_acc)); + else + return variant(); + } + + private: + accessor _acc; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// pointer to member - read only + +template<typename C, typename A> +class property_container<member_object_ptr, A(C::*), void, return_as_ptr, read_only> : public property_container_base +{ + typedef A (C::*accessor); + public: + property_container(const std::string& name, const type declaring_type, accessor acc) + : property_container_base(name, declaring_type), + _acc(acc) + { + static_assert(!std::is_pointer<A>::value, "The given type is already a pointer type!"); + } + + bool is_readonly() const { return true; } + bool is_static() const { return false; } + type get_type() const { return type::get<typename std::add_const<A>::type*>(); } + bool is_array() const { return detail::is_array<A>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + if (C* ptr = object.try_convert<C>()) + return variant(const_cast<const A*>(&(ptr->*_acc))); + else + return variant(); + } + + private: + accessor _acc; +}; + +#endif // __RTTR_PROPERTY_CONTAINER_MEMBER_OBJECT_H__ diff --git a/src/rttr/detail/property_container_object.h b/src/rttr/detail/property_container_object.h new file mode 100644 index 00000000..8d57e1d8 --- /dev/null +++ b/src/rttr/detail/property_container_object.h @@ -0,0 +1,178 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_CONTAINER_OBJECT_H__ +#define __RTTR_PROPERTY_CONTAINER_OBJECT_H__ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global property read write + +template<typename C> +class property_container<object_ptr, C*, void, return_as_copy, set_value> : public property_container_base +{ + public: + property_container(const std::string& name, const type declaring_type, C* pointer) + : property_container_base(name, declaring_type), + _accessor(pointer) + { + } + + bool is_readonly() const { return false; } + bool is_static() const { return true; } + type get_type() const { return type::get<C>(); } + bool is_array() const { return detail::is_array<C>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + if (arg.is_type<C>()) + { + return property_accessor<C>::set_value(*_accessor, arg); + } + else + { + return false; + } + } + + variant get_value(detail::instance& object) const + { + return (variant(*_accessor)); + } + + private: + C* _accessor; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global property read_only + +template<typename C> +class property_container<object_ptr, C*, void, return_as_copy, read_only> : public property_container_base +{ + public: + property_container(const std::string& name, const type declaring_type, C* pointer) + : property_container_base(name, declaring_type), + _accessor(pointer) + { + } + + bool is_readonly() const { return true; } + bool is_static() const { return true; } + type get_type() const { return type::get<C>(); } + bool is_array() const { return detail::is_array<C>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + return (variant(*_accessor)); + } + + private: + C* _accessor; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global property read write + +template<typename C> +class property_container<object_ptr, C*, void, return_as_ptr, set_as_ptr> : public property_container_base +{ + public: + property_container(const std::string& name, const type declaring_type, C* pointer) + : property_container_base(name, declaring_type), + _accessor(pointer) + { + static_assert(!std::is_pointer<C>::value, "The given type is already a pointer type!"); + } + + bool is_readonly() const { return false; } + bool is_static() const { return true; } + type get_type() const { return type::get<C*>(); } + bool is_array() const { return detail::is_array<C>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + if (arg.is_type<C*>()) + { + return property_accessor<C*>::set_value(_accessor, arg); + } + else + { + return false; + } + } + + variant get_value(detail::instance& object) const + { + return (variant(_accessor)); + } + + private: + C* _accessor; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// global property read_only + +template<typename C> +class property_container<object_ptr, C*, void, return_as_ptr, read_only> : public property_container_base +{ + public: + property_container(const std::string& name, const type declaring_type, C* pointer) + : property_container_base(name, declaring_type), + _accessor(pointer) + { + } + + bool is_readonly() const { return true; } + bool is_static() const { return true; } + type get_type() const { return type::get<typename std::add_const<C>::type*>(); } + bool is_array() const { return detail::is_array<C>::value; } + + bool set_value(detail::instance& object, detail::argument& arg) const + { + return false; + } + + variant get_value(detail::instance& object) const + { + return (variant(const_cast<const C*>(_accessor))); + } + + private: + C* _accessor; +}; + +#endif // __RTTR_PROPERTY_CONTAINER_OBJECT_H__ diff --git a/src/rttr/detail/reflection_database.cpp b/src/rttr/detail/reflection_database.cpp new file mode 100644 index 00000000..d677e0ef --- /dev/null +++ b/src/rttr/detail/reflection_database.cpp @@ -0,0 +1,204 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/reflection_database_p.h" + +#include "rttr/detail/constructor_container_base.h" +#include "rttr/detail/destructor_container_base.h" +#include "rttr/detail/enumeration_container_base.h" +#include "rttr/detail/method_container_base.h" +#include "rttr/detail/property_container.h" + +#include <unordered_map> +#include <vector> +#include <memory> +#include <set> +#include <algorithm> + +using namespace std; + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +bool reflection_database::does_signature_match_arguments(const vector<type>& param_list, const vector<type>& args) +{ + if (param_list.size() == args.size()) + { + int index = 0; + for (const auto& arg : args) + { + if (arg != param_list[index]) + { + return false; + } + ++index; + } + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::vector<type> reflection_database::extract_types(const vector<detail::argument>& args) +{ + std::vector<type> result; + for (const auto& arg : args) + { + result.push_back(arg.get_type()); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool reflection_database::is_method_already_registered(const unique_ptr<detail::method_container_base>& method, const method_map& method_container) +{ + const auto ret = method_container.equal_range(method->get_name()); + if (ret.first != method_container.end()) + { + detail::method_container_base* meth = method.get(); + auto found_meth = find_if(ret.first, ret.second, [meth](const method_map::value_type& item) + { + auto curr_meth = reflection_database::instance()._method_list[item.second].get(); + if (does_signature_match_arguments(curr_meth->get_parameter_types(), + meth->get_parameter_types())) + { + return true; + } + return false; + }); + + if (found_meth != ret.second) // method with same signature found + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void reflection_database::register_property(unique_ptr<detail::property_container_base> prop, property_map& property_container) +{ + const auto prop_name = prop->get_name(); + if (property_container.find(prop_name) != property_container.end()) + return; + + reflection_database::instance()._property_list.push_back(move(prop)); + property_container.emplace(prop_name, reflection_database::instance()._property_list.size() - 1); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +// methods that have the same name and signature will be ignored +void reflection_database::register_method(unique_ptr<detail::method_container_base> method, method_map& method_container) +{ + if (is_method_already_registered(method, method_container)) + return; //ignore this method, it's already registered + + auto meth_name = method->get_name(); + reflection_database::instance()._method_list.push_back(move(method)); + method_container.emplace(meth_name, reflection_database::instance()._method_list.size() - 1); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +detail::property_container_base* reflection_database::find_property(const std::string& name, property_map& property_container) +{ + const auto ret = property_container.find(name); + if (ret != property_container.end()) + return reflection_database::instance()._property_list[ret->second].get(); + else + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/*! + * \brief This function searches in \p method_container for a method with name \p name. + * When it founds a method it will return a valid pointer, otherwise a nullptr. + */ +detail::method_container_base* reflection_database::find_method(const std::string& name, const method_map& method_container) +{ + const auto ret = method_container.find(name); + if (ret != method_container.end()) + return reflection_database::instance()._method_list[ret->second].get(); + else + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +/*! + * \brief This function searches in \p method_container for a method with name \p name and with a signature listed in \p args. + * When it founds a method it will return a valid pointer, otherwise a nullptr. + */ +detail::method_container_base* reflection_database::find_method(const std::string& name, const std::vector<type>& params, + const method_map& method_container) +{ + const auto range = method_container.equal_range(name); + auto ret = find_if(range.first, range.second, + [params](const method_map::value_type& item) + { + const auto meth = reflection_database::instance()._method_list[item.second].get(); + if (reflection_database::does_signature_match_arguments(meth->get_parameter_types(), params)) + return true; + else + return false; + } ); + + if (ret != method_container.end()) + return reflection_database::instance()._method_list[ret->second].get(); + else + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void reflection_database::class_data::add_constructor(unique_ptr<detail::constructor_container_base> ctor) +{ + for (const auto& curr_ctor : _ctorList) + { + if (reflection_database::does_signature_match_arguments(curr_ctor.get()->get_parameter_types(), + move(ctor.get()->get_parameter_types()))) + { + return; // don't worry, ctor will destroy itself + } + } + _ctorList.push_back(move(ctor)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/reflection_database_p.h b/src/rttr/detail/reflection_database_p.h new file mode 100644 index 00000000..4f9ed0ed --- /dev/null +++ b/src/rttr/detail/reflection_database_p.h @@ -0,0 +1,140 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_REFLECTION_DATABASE_P_H__ +#define __RTTR_REFLECTION_DATABASE_P_H__ + +#include "rttr/type.h" + +#include <unordered_map> +#include <vector> +#include <string> +#include <memory> + +#define RTTR_MAX_TYPE_COUNT 32767 +#define RTTR_MAX_INHERIT_TYPES_COUNT 50 + +namespace rttr +{ +namespace detail +{ + +/*! + * This class holds all reflection and type data. + * It is not part of the rttr API + */ +class RTTR_LOCAL reflection_database +{ + public: + reflection_database() {}; + static reflection_database& instance() { static reflection_database obj; return obj; } + + typedef void*(*rttr_cast_func)(void*); + typedef variant (*variant_create_func)(void*); + typedef derived_info(*get_derived_info_func)(void*); + + typedef std::unordered_map< std::string, const rttr::type::type_id> NameToTag; + typedef std::vector< std::unique_ptr< property_container_base>> property_container; + typedef std::vector< std::unique_ptr< constructor_container_base>> constructor_container; + typedef std::vector< std::unique_ptr< constructor_container_base>> destructor_container; + typedef std::vector< std::unique_ptr< method_container_base>> method_container; + + typedef std::vector< std::unique_ptr< constructor_container_base>> ctor_list; + typedef std::unordered_map< std::string, property_container::size_type> property_map; + typedef std::unordered_multimap< std::string, method_container::size_type> method_map; + + /*! + * \brief This function returns true, when the given types in \p param_list are the same type like in \p args, + * otherwise false. + */ + static bool does_signature_match_arguments(const std::vector<type>& param_list, const std::vector<type>& args); + + /*! + * \brief This function return from the given list of arguments \p args a list of corresponding type objects. + */ + static std::vector<type> extract_types(const std::vector<detail::argument>& args); + + /*! + * \brief This function returns true, when in \p method_container exists already a method with the same name + * and signature like the given \p method. + */ + static bool is_method_already_registered(const std::unique_ptr<detail::method_container_base>& method, const method_map& method_container); + + + static void register_property(std::unique_ptr<detail::property_container_base> prop, property_map& property_container); + static void register_method(std::unique_ptr<detail::method_container_base> method, method_map& method_container); + + static detail::property_container_base* find_property(const std::string& name, property_map& property_container); + static detail::method_container_base* find_method(const std::string& name, const method_map& method_container); + static detail::method_container_base* find_method(const std::string& name, const std::vector<type>& params, const method_map& method_container); + + struct class_data + { + void add_constructor(std::unique_ptr<detail::constructor_container_base> ctor); + + reflection_database::ctor_list _ctorList; + reflection_database::property_map _property_map; + reflection_database::method_map _method_map; + }; + + private: + reflection_database(const reflection_database& other); + reflection_database& operator=(const reflection_database& other); + + public: + type::type_id type_id_counter; // the global incremented id counter + NameToTag name_to_id; // a container for mapping the name of a type to its unique id + std::string name_list[RTTR_MAX_TYPE_COUNT]; + type::type_id base_class_list[RTTR_MAX_TYPE_COUNT * RTTR_MAX_INHERIT_TYPES_COUNT]; // this list contains for every type its base classes + type::type_id derived_class_list[RTTR_MAX_TYPE_COUNT * RTTR_MAX_INHERIT_TYPES_COUNT]; // this list contains for every type its derived classes + rttr_cast_func conversion_list[RTTR_MAX_TYPE_COUNT * RTTR_MAX_INHERIT_TYPES_COUNT]; // this list contains for every type a conversion function to its base classes + variant_create_func variant_create_func_list[RTTR_MAX_TYPE_COUNT]; // this list contains for every type a create function to a variant + get_derived_info_func get_derived_info_func_list[RTTR_MAX_TYPE_COUNT]; + type::type_id raw_type_list[RTTR_MAX_TYPE_COUNT]; + bool is_class_list[RTTR_MAX_TYPE_COUNT]; + bool is_enum_list[RTTR_MAX_TYPE_COUNT]; + bool is_array_list[RTTR_MAX_TYPE_COUNT]; + bool is_pointer_list[RTTR_MAX_TYPE_COUNT]; + bool is_primitive_list[RTTR_MAX_TYPE_COUNT]; + std::unique_ptr<class_data> class_data_list[RTTR_MAX_TYPE_COUNT]; + std::unique_ptr<constructor_container_base> constructor_list[RTTR_MAX_TYPE_COUNT]; + std::unique_ptr<destructor_container_base> destructor_list[RTTR_MAX_TYPE_COUNT]; + std::unique_ptr<enumeration_container_base> enumeration_list[RTTR_MAX_TYPE_COUNT]; + std::vector<std::unique_ptr<type_converter_base>> type_converter_list[RTTR_MAX_TYPE_COUNT]; + property_map global_properties; + method_map global_methods; + + constructor_container _constructor_list; + destructor_container _destructor_list; + method_container _method_list; + property_container _property_list; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_REFLECTION_DATABASE_P_H__ \ No newline at end of file diff --git a/src/rttr/detail/standard_types.cpp b/src/rttr/detail/standard_types.cpp new file mode 100644 index 00000000..02c92c19 --- /dev/null +++ b/src/rttr/detail/standard_types.cpp @@ -0,0 +1,72 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/standard_types.h" +#include "rttr/reflect" + +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(void) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(bool) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(signed char) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(unsigned char) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(char) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(wchar_t) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(short int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(unsigned short int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(unsigned int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(long int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(unsigned long int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(long long int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(unsigned long long int) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(float) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(double) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(long double) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(std::string) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(std::vector<int>) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(std::vector<float>) +RTTR_DEFINE_STANDARD_TYPE_VARIANTS(std::vector<double>) + +RTTR_REGISTER +{ + using namespace rttr; + + class_<std::string>() + .constructor<>() + .constructor<const std::string&>() + .constructor<const std::string&, unsigned int, unsigned int>() + .constructor<const char*>() + .constructor<const char*, unsigned int>() + .constructor<unsigned int, char>() + .method("length", &std::string::length) + .method("size", &std::string::size) + .method("empty", &std::string::empty) + .method("at", static_cast<const char&(std::string::*)(size_t) const>(&std::string::at)) + .method("data", &std::string::data) + .method("c_str", &std::string::c_str) + .method("operator[]", static_cast<char&(std::string::*)(size_t)>(&std::string::operator[])) + ; +} diff --git a/src/rttr/detail/standard_types.h b/src/rttr/detail/standard_types.h new file mode 100644 index 00000000..98968de4 --- /dev/null +++ b/src/rttr/detail/standard_types.h @@ -0,0 +1,121 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_STANDARD_TYPES_H__ +#define __RTTR_STANDARD_TYPES_H__ + +#include "rttr/type.h" +#include "rttr/rttr_enable.h" +#include "rttr/detail/array_container.h" + +#include <string> +#include <vector> + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(void) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(bool) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(signed char) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(unsigned char) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(char) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(wchar_t) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(short int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(unsigned short int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(unsigned int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(long int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(unsigned long int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(long long int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(unsigned long long int) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(float) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(double) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(long double) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(std::string) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(std::vector<int>) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(std::vector<float>) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(std::vector<double>) + +RTTR_DECLARE_TYPE(char[1 ]) +RTTR_DECLARE_TYPE(char[2 ]) +RTTR_DECLARE_TYPE(char[3 ]) +RTTR_DECLARE_TYPE(char[4 ]) +RTTR_DECLARE_TYPE(char[5 ]) +RTTR_DECLARE_TYPE(char[6 ]) +RTTR_DECLARE_TYPE(char[7 ]) +RTTR_DECLARE_TYPE(char[8 ]) +RTTR_DECLARE_TYPE(char[9 ]) +RTTR_DECLARE_TYPE(char[10]) +RTTR_DECLARE_TYPE(char[11]) +RTTR_DECLARE_TYPE(char[12]) +RTTR_DECLARE_TYPE(char[13]) +RTTR_DECLARE_TYPE(char[14]) +RTTR_DECLARE_TYPE(char[15]) +RTTR_DECLARE_TYPE(char[16]) +RTTR_DECLARE_TYPE(char[17]) +RTTR_DECLARE_TYPE(char[18]) +RTTR_DECLARE_TYPE(char[19]) +RTTR_DECLARE_TYPE(char[20]) +RTTR_DECLARE_TYPE(char[21]) +RTTR_DECLARE_TYPE(char[22]) +RTTR_DECLARE_TYPE(char[23]) +RTTR_DECLARE_TYPE(char[24]) +RTTR_DECLARE_TYPE(char[25]) +RTTR_DECLARE_TYPE(char[26]) +RTTR_DECLARE_TYPE(char[27]) +RTTR_DECLARE_TYPE(char[28]) +RTTR_DECLARE_TYPE(char[29]) +RTTR_DECLARE_TYPE(char[30]) +RTTR_DECLARE_TYPE(char[31]) +RTTR_DECLARE_TYPE(char[32]) +RTTR_DECLARE_TYPE(char[33]) +RTTR_DECLARE_TYPE(char[34]) +RTTR_DECLARE_TYPE(char[35]) +RTTR_DECLARE_TYPE(char[36]) +RTTR_DECLARE_TYPE(char[37]) +RTTR_DECLARE_TYPE(char[38]) +RTTR_DECLARE_TYPE(char[39]) +RTTR_DECLARE_TYPE(char[40]) +RTTR_DECLARE_TYPE(char[41]) +RTTR_DECLARE_TYPE(char[42]) +RTTR_DECLARE_TYPE(char[43]) +RTTR_DECLARE_TYPE(char[44]) +RTTR_DECLARE_TYPE(char[45]) +RTTR_DECLARE_TYPE(char[46]) +RTTR_DECLARE_TYPE(char[47]) +RTTR_DECLARE_TYPE(char[48]) +RTTR_DECLARE_TYPE(char[49]) +RTTR_DECLARE_TYPE(char[50]) +RTTR_DECLARE_TYPE(char[51]) +RTTR_DECLARE_TYPE(char[52]) +RTTR_DECLARE_TYPE(char[53]) +RTTR_DECLARE_TYPE(char[54]) +RTTR_DECLARE_TYPE(char[55]) +RTTR_DECLARE_TYPE(char[56]) +RTTR_DECLARE_TYPE(char[57]) +RTTR_DECLARE_TYPE(char[58]) +RTTR_DECLARE_TYPE(char[59]) +RTTR_DECLARE_TYPE(char[60]) + +#endif // __RTTR_STANDARD_TYPES_H__ \ No newline at end of file diff --git a/src/rttr/detail/standard_types_char.cpp b/src/rttr/detail/standard_types_char.cpp new file mode 100644 index 00000000..7095d8d5 --- /dev/null +++ b/src/rttr/detail/standard_types_char.cpp @@ -0,0 +1,90 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/standard_types.h" +#include "rttr/reflect" + +RTTR_DEFINE_TYPE(char[1 ]) +RTTR_DEFINE_TYPE(char[2 ]) +RTTR_DEFINE_TYPE(char[3 ]) +RTTR_DEFINE_TYPE(char[4 ]) +RTTR_DEFINE_TYPE(char[5 ]) +RTTR_DEFINE_TYPE(char[6 ]) +RTTR_DEFINE_TYPE(char[7 ]) +RTTR_DEFINE_TYPE(char[8 ]) +RTTR_DEFINE_TYPE(char[9 ]) +RTTR_DEFINE_TYPE(char[10]) +RTTR_DEFINE_TYPE(char[11]) +RTTR_DEFINE_TYPE(char[12]) +RTTR_DEFINE_TYPE(char[13]) +RTTR_DEFINE_TYPE(char[14]) +RTTR_DEFINE_TYPE(char[15]) +RTTR_DEFINE_TYPE(char[16]) +RTTR_DEFINE_TYPE(char[17]) +RTTR_DEFINE_TYPE(char[18]) +RTTR_DEFINE_TYPE(char[19]) +RTTR_DEFINE_TYPE(char[20]) +RTTR_DEFINE_TYPE(char[21]) +RTTR_DEFINE_TYPE(char[22]) +RTTR_DEFINE_TYPE(char[23]) +RTTR_DEFINE_TYPE(char[24]) +RTTR_DEFINE_TYPE(char[25]) +RTTR_DEFINE_TYPE(char[26]) +RTTR_DEFINE_TYPE(char[27]) +RTTR_DEFINE_TYPE(char[28]) +RTTR_DEFINE_TYPE(char[29]) +RTTR_DEFINE_TYPE(char[30]) +RTTR_DEFINE_TYPE(char[31]) +RTTR_DEFINE_TYPE(char[32]) +RTTR_DEFINE_TYPE(char[33]) +RTTR_DEFINE_TYPE(char[34]) +RTTR_DEFINE_TYPE(char[35]) +RTTR_DEFINE_TYPE(char[36]) +RTTR_DEFINE_TYPE(char[37]) +RTTR_DEFINE_TYPE(char[38]) +RTTR_DEFINE_TYPE(char[39]) +RTTR_DEFINE_TYPE(char[40]) +RTTR_DEFINE_TYPE(char[41]) +RTTR_DEFINE_TYPE(char[42]) +RTTR_DEFINE_TYPE(char[43]) +RTTR_DEFINE_TYPE(char[44]) +RTTR_DEFINE_TYPE(char[45]) +RTTR_DEFINE_TYPE(char[46]) +RTTR_DEFINE_TYPE(char[47]) +RTTR_DEFINE_TYPE(char[48]) +RTTR_DEFINE_TYPE(char[49]) +RTTR_DEFINE_TYPE(char[50]) +RTTR_DEFINE_TYPE(char[51]) +RTTR_DEFINE_TYPE(char[52]) +RTTR_DEFINE_TYPE(char[53]) +RTTR_DEFINE_TYPE(char[54]) +RTTR_DEFINE_TYPE(char[55]) +RTTR_DEFINE_TYPE(char[56]) +RTTR_DEFINE_TYPE(char[57]) +RTTR_DEFINE_TYPE(char[58]) +RTTR_DEFINE_TYPE(char[59]) +RTTR_DEFINE_TYPE(char[60]) diff --git a/src/rttr/detail/std_conversion_functions.cpp b/src/rttr/detail/std_conversion_functions.cpp new file mode 100644 index 00000000..9ebddd53 --- /dev/null +++ b/src/rttr/detail/std_conversion_functions.cpp @@ -0,0 +1,319 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/std_conversion_functions.h" + +#include <sstream> +#include <locale> +#include <limits> +#include <algorithm> +#include <climits> +#include <iomanip> + +namespace rttr +{ +namespace detail +{ + +//*************************************************************************************** + +bool char_to_bool(const char* source, bool* ok) +{ + return string_to_bool(source, ok); +} + +//*************************************************************************************** + +int char_to_int(const char* source, bool* ok) +{ + char *end_ptr; + errno = 0; + long n = strtol(source, &end_ptr, 0); + + if (ERANGE != errno && (n > INT_MIN && n < INT_MAX) && + end_ptr != source && *end_ptr == '\0') + { + if (ok) + *ok = true; + return static_cast<int>(n); + } + + if (ok) + *ok = false; + return 0; +} + +//*************************************************************************************** + +long long char_to_long_long(const char* source, bool* ok) +{ + char *end_ptr; + errno = 0; + long long n = strtoll(source, &end_ptr, 0); + if (errno != ERANGE && + end_ptr != source && + *end_ptr == '\0') + { + if (ok) + *ok = true; + return n; + } + + if (ok) + *ok = false; + return 0; +} + +//*************************************************************************************** + +unsigned int char_to_uint(const char* source, bool* ok) +{ + char *end_ptr; + errno = 0; + long n = strtoul(source, &end_ptr, 0); + if (errno != ERANGE && + end_ptr != source && + *end_ptr == '\0') + { + if (ok) + *ok = true; + return static_cast<unsigned int>(n); + } + + if (ok) + *ok = false; + return 0; +} + +//*************************************************************************************** + +float char_to_float(const char* source, bool* ok) +{ + char *end_ptr; + errno = 0; + float n = strtof(source, &end_ptr); + + if (errno != ERANGE && + end_ptr != source && + *end_ptr == '\0') + { + if (ok) + *ok = true; + return n; + } + + if (ok) + *ok = false; + return 0.0f; +} + +//*************************************************************************************** + +double char_to_double(const char* source, bool* ok) +{ + char *end_ptr; + errno = 0; + double n = strtod(source, &end_ptr); + + if (errno != ERANGE && + end_ptr != source && + *end_ptr == '\0') + { + if (ok) + *ok = true; + return n; + } + + if (ok) + *ok = false; + return 0.0; +} + +//*************************************************************************************** + +std::string int_to_string(int value, bool* ok) +{ + try + { + std::string text = std::to_string(value); + if (ok) + *ok = true; + return text; + } + catch (...) + { + if (ok) + *ok = false; + return std::string(); + } +} + +//*************************************************************************************** + +std::string float_to_string(float value, bool* ok) +{ + try + { + std::stringstream ss; + ss << std::setprecision(std::numeric_limits<float>::digits10) << value; + if (ok) + *ok = true; + return ss.str(); + } + catch (...) + { + if (ok) + *ok = false; + return std::string(); + } +} + +//*************************************************************************************** + +std::string double_to_string(double value, bool* ok) +{ + try + { + std::stringstream ss; + ss << std::setprecision(std::numeric_limits<double>::digits10) << value; + if (ok) + *ok = true; + return ss.str(); + } + catch (...) + { + if (ok) + *ok = false; + return std::string(); + } +} + +//*************************************************************************************** + +bool string_to_bool(std::string text, bool* ok) +{ + std::transform(text.begin(), text.end(), text.begin(), ::tolower); + text.erase( std::remove_if( text.begin(), text.end(), []( char ch ) { return std::isspace<char>( ch, std::locale::classic() ); } ), text.end() ); + + if (text == "false" || text == "0" || text.empty()) + { + if (ok) + *ok = true; + return false; + } + + if (ok) + *ok = true; + + return true; +} + +//*************************************************************************************** + +int string_to_int(const std::string& source, bool* ok) +{ + try + { + std::size_t pos = 0; + const int value = std::stoi(source, &pos); + if (pos == source.length()) + { + if (ok) + *ok = true; + return value; + } + } + catch (...) + { + if (ok) + *ok = false; + return 0; + } + + if (ok) + *ok = false; + return 0; +} + +//*************************************************************************************** + +float string_to_float(const std::string& source, bool* ok) +{ + try + { + std::size_t pos = 0; + const float value = std::stof(source, &pos); + if (pos == source.length()) + { + if (ok) + *ok = true; + return value; + } + } + catch (...) + { + if (ok) + *ok = false; + return 0; + } + + if (ok) + *ok = false; + return 0; +} + +//*************************************************************************************** + +double string_to_double(const std::string& source, bool* ok) +{ + try + { + std::size_t pos = 0; + const double value = std::stod(source, &pos); + if (pos == source.length()) + { + if (ok) + *ok = true; + return value; + } + } + catch (...) + { + if (ok) + *ok = false; + return 0; + } + + if (ok) + *ok = false; + + return 0; +} + + +} // end namespace detail +} // end namespace rttr diff --git a/src/rttr/detail/std_conversion_functions.h b/src/rttr/detail/std_conversion_functions.h new file mode 100644 index 00000000..bc9bec8e --- /dev/null +++ b/src/rttr/detail/std_conversion_functions.h @@ -0,0 +1,94 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_STD_CONVERSION_FUNCTIONS_H__ +#define __RTTR_STD_CONVERSION_FUNCTIONS_H__ + +#include "rttr/base/core_prerequisites.h" +#include <string> + +namespace rttr +{ +namespace detail +{ + +//*************************************************************************************** + +RTTR_API bool char_to_bool(const char* source, bool* ok); + +//*************************************************************************************** + +RTTR_API int char_to_int(const char* source, bool* ok); + +//*************************************************************************************** + +RTTR_API long long char_to_long_long(const char* source, bool& ok); + +//*************************************************************************************** + +RTTR_API unsigned int char_to_uint(const char* source, bool& ok); + +//*************************************************************************************** + +RTTR_API float char_to_float(const char* source, bool* ok); + +//*************************************************************************************** + +RTTR_API double char_to_double(const char* source, bool* ok); + +//*************************************************************************************** + +RTTR_API std::string int_to_string(int value, bool* ok); + +//*************************************************************************************** + +RTTR_API std::string float_to_string(float value, bool* ok); + +//*************************************************************************************** + +RTTR_API std::string double_to_string(double value, bool* ok); + +//*************************************************************************************** + +RTTR_API bool string_to_bool(std::string value, bool* ok); + +//*************************************************************************************** + +RTTR_API int string_to_int(const std::string& source, bool* ok); + +//*************************************************************************************** + +RTTR_API float string_to_float(const std::string& source, bool* ok); + +//*************************************************************************************** + +RTTR_API double string_to_double(const std::string& source, bool* ok); + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_STD_CONVERSION_FUNCTIONS_H__ diff --git a/src/rttr/detail/type_converter.h b/src/rttr/detail/type_converter.h new file mode 100644 index 00000000..e48a44d3 --- /dev/null +++ b/src/rttr/detail/type_converter.h @@ -0,0 +1,77 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TYPE_CONVERTER_H__ +#define __RTTR_TYPE_CONVERTER_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/detail/array_mapper.h" +#include "rttr/variant.h" + +namespace rttr +{ + +namespace detail +{ + +struct RTTR_LOCAL type_converter_base +{ + type_converter_base(const type& target_type) : _target_type(target_type) {} + virtual variant to_variant(void* data, bool& ok) const = 0; + virtual ~type_converter_base() {} + + type _target_type; +}; + +template<typename TargetType> +struct type_converter_target : type_converter_base +{ + type_converter_target(const type& target_type) : type_converter_base(target_type) {} + virtual ~type_converter_target() {} + variant to_variant(void* data, bool& ok) const { return convert(data, ok); } + virtual TargetType convert(void* data, bool& ok) const = 0; +}; + +template<typename TargetType, typename SourceType, typename F> +struct type_converter : type_converter_target<TargetType> +{ + type_converter(const F& acc) : type_converter_target<TargetType>(type::get<TargetType>()), _acc(acc) { } + virtual ~type_converter() {} + + TargetType convert(void* data, bool& ok) const + { + SourceType* obj = static_cast<SourceType*>(data); + return _acc(*obj, ok); + } + + F _acc; +}; + +} // end namespace detail +} // end namespace rttr + +#endif // __RTTR_TYPE_CONVERTER_H__ diff --git a/src/rttr/detail/utility.h b/src/rttr/detail/utility.h new file mode 100644 index 00000000..015906a0 --- /dev/null +++ b/src/rttr/detail/utility.h @@ -0,0 +1,193 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_UTILITY_H__ +#define __RTTR_UTILITY_H__ + +#include "rttr/base/core_prerequisites.h" + +namespace rttr +{ +namespace detail +{ + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// This will add the c++14 integer sequence to c++11 + + template <class T, T... I> + struct integer_sequence + { + template <T N> using append = integer_sequence<T, I..., N>; + static std::size_t size() { return sizeof...(I); } + using next = append<sizeof...(I)>; + using type = T; + }; + + template <class T, T Index, std::size_t N> + struct sequence_generator + { + using type = typename sequence_generator<T, Index - 1, N - 1>::type::next; + }; + + template <class T, T Index> + struct sequence_generator<T, Index, 0ul> { using type = integer_sequence<T>; }; + + template <std::size_t... I> + using index_sequence = integer_sequence<std::size_t, I...>; + +#if RTTR_COMPILER == RTTR_COMPILER_MSVC && RTTR_COMP_VER <= 1800 + // workaround for a compiler bug of nested aliases (#1085630) + template <class T, T N> + struct make_integer_sequence_impl + { + typedef typename sequence_generator<T, N, N>::type type; + }; + + template <class T, T N> + struct make_index_sequence_impl + { + typedef typename make_integer_sequence_impl<T, N>::type type; + }; + + template <class T, T N> + using make_integer_sequence = typename make_integer_sequence_impl<T, N>::type; + + template <std::size_t N> + using make_index_sequence = typename make_integer_sequence_impl<std::size_t, N>::type; +#else + template <class T, T N> + using make_integer_sequence = typename sequence_generator<T, N, N>::type; + + template <std::size_t N> + using make_index_sequence = make_integer_sequence<std::size_t, N>; +#endif + + template<class... T> + using index_sequence_for = make_index_sequence<sizeof...(T)>; + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +struct remove_first_index_impl +{ + using type = index_sequence<>; +}; + +template<std::size_t First, std::size_t... I> +struct remove_first_index_impl<detail::index_sequence<First, I...>> +{ + using type = detail::index_sequence<I...>; +}; + +template<typename T> +using remove_first_index = typename remove_first_index_impl<T>::type; + + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename, typename> +struct concat_index_sequence { }; + +template<std::size_t... Ts, std::size_t... Us> +struct concat_index_sequence<index_sequence<Ts...>, index_sequence<Us...>> +{ + using type = index_sequence<Ts..., Us...>; +}; + +template <class T> +struct remove_last_index_impl; + +template <size_t Last> +struct remove_last_index_impl<index_sequence<Last>> +{ + using type = index_sequence<>; +}; + +template<std::size_t First, std::size_t... I> +struct remove_last_index_impl<index_sequence<First, I...>> +{ + using type = typename concat_index_sequence< + index_sequence<First>, + typename remove_last_index_impl<index_sequence<I...>>::type + >::type; +}; + +template<typename T> +using remove_last_index = typename remove_last_index_impl<T>::type; + + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// This will add the c++14 integer sequence to c++11 + + +static RTTR_FORCE_INLINE bool check_all_true() { return true; } + +template<typename... BoolArgs> +static RTTR_INLINE bool check_all_true(bool arg1, BoolArgs... args) { return arg1 & check_all_true(args...); } + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// copy the content of any arbitrary array, use it like: +// copy_array(in, out); +// works with every dimension + +template<typename ElementType> +struct copy_array_helper_impl +{ + void operator()(const ElementType &in, ElementType &out) + { + out = in; + } +}; + +template<typename ElementType, std::size_t Count> +struct copy_array_helper_impl<ElementType[Count]> { + void operator()(const ElementType (&in)[Count], ElementType (&out)[Count]) + { + for(std::size_t i = 0; i < Count; ++i) + copy_array_helper_impl<ElementType>()(in[i], out[i]); + } +}; + +template<typename ElementType, std::size_t Count> +auto copy_array(const ElementType (&in)[Count], ElementType (&out)[Count]) + -> ElementType (&)[Count] +{ + copy_array_helper_impl<ElementType[Count]>()(in, out); + return out; +} + +} // end namespace detail +} // end namespace rttr + +#endif //__RTTR_UTILITY_H__ diff --git a/src/rttr/enumeration.cpp b/src/rttr/enumeration.cpp new file mode 100644 index 00000000..9d3ecf3b --- /dev/null +++ b/src/rttr/enumeration.cpp @@ -0,0 +1,167 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/enumeration.h" +#include "rttr/detail/enumeration_container_base.h" +#include "rttr/detail/argument.h" + +#include <utility> + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration::enumeration(const detail::enumeration_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool enumeration::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration::operator bool() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type enumeration::get_underlying_type() const +{ + if (is_valid()) + return _container->get_underlying_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type enumeration::get_type() const +{ + if (is_valid()) + return _container->get_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type enumeration::get_declaring_type() const +{ + if (is_valid()) + return _container->get_declaring_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant enumeration::get_metadata(int key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant enumeration::get_metadata(const std::string& key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<string> enumeration::get_keys() const +{ + if (is_valid()) + return _container->get_keys(); + else + return vector<string>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<variant> enumeration::get_values() const +{ + if (is_valid()) + return _container->get_values(); + else + return vector<variant>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string enumeration::value_to_key(detail::argument value) const +{ + if (is_valid()) + return _container->value_to_key(value); + else + return string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant enumeration::key_to_value(const std::string& key) const +{ + if (is_valid()) + return _container->key_to_value(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool enumeration::operator==(const enumeration& other) const +{ + return (_container == other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool enumeration::operator!=(const enumeration& other) const +{ + return (_container != other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/enumeration.h b/src/rttr/enumeration.h new file mode 100644 index 00000000..a0196488 --- /dev/null +++ b/src/rttr/enumeration.h @@ -0,0 +1,222 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_ENUMERATION_H__ +#define __RTTR_ENUMERATION_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/type.h" + +#include <memory> +#include <string> + +namespace rttr +{ +class type; +class Variant; + +namespace detail +{ +class enumeration_container_base; +class argument; +} +/*! + * The \ref enumeration class provides several meta information about an enum. + * + * A instance of an enumeration class can only be obtained from the \ref type class. + * See \ref type::get_enum() and \ref type::get_enumerations(). + * + * For registration an enum, nested inside a class, see \ref class_::enumeration() and for global enums see \ref register_global::enumeration. + * + * Meta Information + * ---------------- + * An \ref enumeration is described by it's name (\ref get_name()), it's keys (\ref get_keys()) and it's corresponding values (\ref get_values()). + * The key is represented as std::string and the values are stored as the underlying enum value. + * When the \ref enumeration was declared inside a class, then \ref get_declaring_type() can be used to obtain the type of this class. + * + * The conversion functions \ref key_to_value(), \ref value_to_key() allow conversion between the value representation of an enumeration and its literal representation. + * + * Copying and Assignment + * ---------------------- + * A \ref enumeration object is lightweight and can be copied by value. However, each copy will refer to the same underlying enumeration. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + using namespace rttr; + struct MyStruct + { + enum E_Alignment + { + AlignLeft = 0x0001, + AlignRight = 0x0002, + AlignHCenter = 0x0004, + AlignJustify = 0x0008 + }; + }; + enumeration enum_align = type::get("MyStruct").get_enumeration("E_Alignment"); + if (enum_align) + { + MyStruct::E_Alignment enum_value = MyStruct::AlignLeft; + std::string key = enum_align.value_to_key(enum_value); + std::cout << key; // prints "AlignLeft" + + variant var = enum_align.key_to_value("AlignJustify"); + std::cout << var.get_value<MyStruct::E_Alignment>(); // prints "8"; + } +\endcode + * + * \see method, property, constructor and type + */ +class RTTR_API enumeration +{ + public: + /*! + * \brief Returns true if this \ref enumeration is valid, otherwise false. + * + * \return True if this \ref enumeration is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this \ref enumeration is valid or not. + * + * \return True if this \ref enumeration is valid, otherwise false. + */ + operator bool() const; + + /*! + * \brief Returns the name of this \ref enumeration. + * + * \return Name of the \ref enumeration. + */ + std::string get_name() const; + + /*! + * \brief Returns the underlying type (int, unsigned int, etc.) of this \ref enumeration. + * + * \return Data type of the \ref enumeration. + */ + type get_underlying_type() const; + + /*! + * \brief Returns the type object of this \ref enumeration. + * + * \return Data type of the \ref enumeration. + */ + type get_type() const; + + /*! + * \brief Returns the \ref type of the class or struct that declares this \ref enumeration. + * + * \remark When this enumeration does not belong to a class (i.e. is a global enumeration) it will return an invalid type object. + * When this enumeration is not valid, this function will return an invalid type object (see \ref type::is_valid). + * + * \return \ref type "Type" of the declaring class/struct for this enumeration. + */ + type get_declaring_type() const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(int key) const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(const std::string& key) const; + + /*! + * \brief Returns all enum keys registered for this enumeration. + * + * \remark When the enumeration is invalid then an empty vector is returned. + * + * \return A vector of enumeration keys. + */ + std::vector<std::string> get_keys() const; + + + /*! + * \brief Returns all enum values registered for this enumeration. + * + * \remark When the enumeration is invalid then an empty vector is returned. + * + * \return A vector of enumeration values. + */ + std::vector<variant> get_values() const; + + /*! + * \brief Returns the string that is used as the name of the given enumeration \p value, + * or an empty string if the \p value is not defined. + * + * \return A std::string object, containing the key for the given value. + */ + std::string value_to_key(detail::argument value) const; + + /*! + * \brief Returns the value of the given enumeration key, or an empty variant if the key is not defined. + * + * \return A variant object, containing the value for the given key. + */ + variant key_to_value(const std::string& key) const; + + /*! + * \brief Returns true if this enumeration is the same like the \p other. + * + * \return True if both enumerations are equal, otherwise false. + */ + bool operator==(const enumeration& other) const; + + /*! + * Returns true if this enumeration is the not the same like the \p other. + * + * \return True if both enumerations are different, otherwise false. + */ + bool operator!=(const enumeration& other) const; + + private: + friend class type; // to prevent creation of this class + //! Constructs a valid MetaProperty from a PropertyContainerBase. + enumeration(const detail::enumeration_container_base* container = nullptr); + private: + const detail::enumeration_container_base* _container; +}; + +} // end namespace rttr + +#endif // __RTTR_ENUMERATION_H__ diff --git a/src/rttr/impl/register_reflection_impl.h b/src/rttr/impl/register_reflection_impl.h new file mode 100644 index 00000000..4ed91085 --- /dev/null +++ b/src/rttr/impl/register_reflection_impl.h @@ -0,0 +1,637 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_REGISTER_REFLECTION_IMPL_H__ +#define __RTTR_REGISTER_REFLECTION_IMPL_H__ + +#include "rttr/detail/constructor_container.h" +#include "rttr/detail/destructor_container.h" +#include "rttr/detail/enumeration_container.h" +#include "rttr/detail/method_container.h" +#include "rttr/detail/property_container.h" +#include "rttr/detail/accessor_type.h" +#include "rttr/detail/misc_type_traits.h" +#include "rttr/detail/utility.h" + +#include <type_traits> +#include <memory> + +namespace rttr +{ + +namespace impl +{ +// helper method to store the metadata +template<typename T> +void store_metadata(T& obj, std::vector<rttr::metadata> data) +{ + for (auto& item : data) + { + if (item.get_key().is_type<int>()) + obj->set_metadata(item.get_key().get_value<int>(), item.get_value()); + else if (item.get_key().is_type<std::string>()) + obj->set_metadata(item.get_key().get_value<std::string>(), item.get_value()); + } +} + +} // end namespace impl + +///////////////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +template<typename ClassType, typename... Args> +void constructor_impl(std::vector< rttr::metadata > data) +{ + using namespace std; + const type type = type::get<ClassType>(); + unique_ptr<detail::constructor_container_base> ctor(new detail::constructor_container<ClassType, Args...>()); + // register the type with the following call: + ctor->get_instanciated_type(); + ctor->get_parameter_types(); + + impl::store_metadata(ctor, data); + impl::register_constructor(type, move(ctor)); + impl::register_destructor(type, unique_ptr<detail::destructor_container_base>(new detail::destructor_container<ClassType>())); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename A, typename Policy> +void property_impl(const std::string& name, A accessor, std::vector<rttr::metadata> data, const Policy&) +{ + using namespace std; + using getter_policy = typename detail::get_getter_policy<Policy>::type; + using setter_policy = typename detail::get_setter_policy<Policy>::type; + using acc_type = typename detail::property_type<A>::type; + + unique_ptr<detail::property_container_base> prop(new detail::property_container<acc_type, A, void, getter_policy, setter_policy>(name, type::get<ClassType>(), accessor)); + // register the type with the following call: + prop->get_type(); + impl::store_metadata(prop, data); + impl::register_property(type::get<ClassType>(), move(prop)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename A1, typename A2, typename Policy> +void property_impl(const std::string& name, A1 getter, A2 setter, std::vector<rttr::metadata> data, const Policy&) +{ + using namespace std; + using getter_policy = typename detail::get_getter_policy<Policy>::type; + using setter_policy = typename detail::get_setter_policy<Policy>::type; + using acc_type = typename detail::property_type<A1>::type; + + unique_ptr<detail::property_container_base> prop(new detail::property_container<acc_type, A1, A2, getter_policy, setter_policy>(name, type::get<ClassType>(), getter, setter)); + // register the type with the following call: + prop->get_type(); + impl::store_metadata(prop, data); + impl::register_property(type::get<ClassType>(), move(prop)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename A, typename Policy> +void property_readonly_impl(const std::string& name, A accessor, std::vector<rttr::metadata> data, const Policy&) +{ + using namespace std; + using getter_policy = typename detail::get_getter_policy<Policy>::type; + using setter_policy = detail::read_only; + using acc_type = typename detail::property_type<A>::type; + + unique_ptr<detail::property_container_base> prop(new detail::property_container<acc_type, A, void, getter_policy, setter_policy>(name, type::get<ClassType>(), accessor)); + // register the type with the following call: + prop->get_type(); + impl::store_metadata(prop, data); + impl::register_property(type::get<ClassType>(), move(prop)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename F, typename Policy> +void method_impl(const std::string& name, F function, std::vector< rttr::metadata > data, const Policy&) +{ + using namespace std; + using method_policy = typename detail::get_method_policy<Policy>::type; + + unique_ptr<detail::method_container_base> meth(new detail::method_container<F, method_policy>(name, type::get<ClassType>(), function)); + // register the underlying type with the following call: + meth->get_return_type(); + meth->get_parameter_types(); + impl::store_metadata(meth, data); + impl::register_method(type::get<ClassType>(), move(meth)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType, typename EnumType> +void enumeration_impl(std::vector< std::pair< std::string, EnumType> > enum_data, std::vector<rttr::metadata> data) +{ + using namespace std; + static_assert(is_enum<EnumType>::value, "No enum type provided, please call this method with an enum type!"); + type declar_type = impl::get_invalid_type(); + if (!std::is_same<ClassType, void>::value) + declar_type = type::get<ClassType>(); + + unique_ptr<detail::enumeration_container_base> enum_item(new detail::enumeration_container<EnumType>(declar_type, move(enum_data))); + // register the underlying type with the following call: + enum_item->get_type(); + + impl::store_metadata(enum_item, data); + impl::register_enumeration(type::get<EnumType>(), move(enum_item)); +} + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +void constructor_(std::vector< rttr::metadata > data) +{ + impl::constructor_impl<T>(std::move(data)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A> +void property_(const std::string& name, A acc) +{ + using namespace std; + static_assert(is_pointer<A>::value, "No valid property accessor provided!"); + impl::property_impl<void, A>(name, acc, std::vector< rttr::metadata >(), + detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A> +void property_(const std::string& name, A acc, std::vector< rttr::metadata > data) +{ + using namespace std; + static_assert(is_pointer<A>::value, "No valid property accessor provided!"); + impl::property_impl<void, A>(name, acc, std::move(data), detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A, typename Policy> +void property_(const std::string& name, A acc, + const Policy& policy, typename std::enable_if<detail::contains<Policy, detail::policy_list>::value>::type*) +{ + using namespace std; + static_assert(is_pointer<A>::value, "No valid property accessor provided!"); + impl::property_impl<void, A>(name, acc, std::vector< rttr::metadata >(), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A, typename Policy> +void property_(const std::string& name, A acc, std::vector< rttr::metadata > data, + const Policy& policy) +{ + using namespace std; + static_assert(is_pointer<A>::value, "No valid property accessor provided!"); + impl::property_impl<void, A>(name, acc, std::move(data), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A1, typename A2> +void property_(const std::string& name, A1 getter, A2 setter, + typename std::enable_if<!detail::contains<A2, detail::policy_list>::value>::type*) +{ + using namespace std; + static_assert(is_pointer<A1>::value || is_pointer<A2>::value || + detail::is_function_ptr<A1>::value || detail::is_function_ptr<A2>::value || + detail::is_std_function<A1>::value || detail::is_std_function<A2>::value, + "No valid property accessor provided!"); + impl::property_impl<void, A1, A2>(name, getter, setter, std::vector< rttr::metadata >(), + detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A1, typename A2> +void property_(const std::string& name, A1 getter, A2 setter, std::vector< rttr::metadata > data) +{ + using namespace std; + static_assert(is_pointer<A1>::value || is_pointer<A2>::value || + detail::is_function_ptr<A1>::value || detail::is_function_ptr<A2>::value || + detail::is_std_function<A1>::value || detail::is_std_function<A2>::value, + "No valid property accessor provided!"); + impl::property_impl<void, A1, A2>(name, getter, setter, move(data), + detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A1, typename A2, typename Policy> +void property_(const std::string& name, A1 getter, A2 setter, + const Policy& policy) +{ + using namespace std; + static_assert(is_pointer<A1>::value || is_pointer<A2>::value || + detail::is_function_ptr<A1>::value || detail::is_function_ptr<A2>::value || + detail::is_std_function<A1>::value || detail::is_std_function<A2>::value, + "No valid property accessor provided!"); + impl::property_impl<void, A1, A2>(name, getter, setter, std::vector< rttr::metadata >(), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A1, typename A2, typename Policy> +void property_(const std::string& name, A1 getter, A2 setter, std::vector< rttr::metadata > data, + const Policy& policy) +{ + using namespace std; + static_assert(is_pointer<A1>::value || is_pointer<A2>::value || + detail::is_function_ptr<A1>::value || detail::is_function_ptr<A2>::value || + detail::is_std_function<A1>::value || detail::is_std_function<A2>::value, + "No valid property accessor provided!"); + impl::property_impl<void, A1, A2>(name, getter, setter, std::move(data), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A> +void property_readonly_(const std::string& name, A acc) +{ + using namespace std; + static_assert(is_pointer<A>::value || detail::is_function_ptr<A>::value || detail::is_std_function<A>::value, + "No valid property accessor provided!"); + impl::property_readonly_impl<void, A>(name, acc, std::vector< rttr::metadata >(), + detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A> +void property_readonly_(const std::string& name, A acc, std::vector< rttr::metadata > data) +{ + using namespace std; + static_assert(is_pointer<A>::value || detail::is_function_ptr<A>::value || detail::is_std_function<A>::value, + "No valid property accessor provided!"); + impl::property_readonly_impl<void, A>(name, acc, std::move(data), + detail::default_property_policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A, typename Policy> +void property_readonly_(const std::string& name, A acc, const Policy& policy) +{ + using namespace std; + static_assert(is_pointer<A>::value || detail::is_function_ptr<A>::value || detail::is_std_function<A>::value, + "No valid property accessor provided!"); + impl::property_readonly_impl<void, A>(name, acc, std::vector< rttr::metadata >(), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename A, typename Policy> +void property_readonly_(const std::string& name, A acc, std::vector< rttr::metadata > data, const Policy& policy) +{ + using namespace std; + static_assert(is_pointer<A>::value || detail::is_function_ptr<A>::value || detail::is_std_function<A>::value, + "No valid property accessor provided!"); + impl::property_readonly_impl<void, A>(name, acc, std::move(data), + policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename F> +void method_(const std::string& name, F function) +{ + impl::method_impl<void>(name, function, std::vector< rttr::metadata >(), detail::default_invoke()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename F> +void method_(const std::string& name, F function, std::vector< rttr::metadata > data) +{ + impl::method_impl<void>(name, function, std::move(data), detail::default_invoke()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename F, typename Policy> +void method_(const std::string& name, F function, const Policy& policy) +{ + impl::method_impl<void>(name, function, std::vector< rttr::metadata >(), policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename F, typename Policy> +void method_(const std::string& name, F function, std::vector< rttr::metadata > data, const Policy& policy) +{ + impl::method_impl<void>(name, function, std::move(data), policy); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename EnumType> +void enumeration_(std::vector< std::pair< std::string, EnumType> > enum_data, std::vector<rttr::metadata> data) +{ + impl::enumeration_impl<void, EnumType>(std::move(enum_data), std::move(data)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +class_<ClassType>::class_(std::vector< rttr::metadata > data) +{ + static_assert(std::is_class<ClassType>::value, "Reflected type is not a class or struct."); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +class_<ClassType>::~class_() +{ + // make sure that all base classes are registered + impl::base_classes<ClassType>::get_types(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename... Args> +class_<ClassType>& class_<ClassType>::constructor(std::vector<rttr::metadata> data) +{ + impl::constructor_impl<ClassType, Args...>(std::move(data)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A acc) +{ + static_assert(std::is_member_object_pointer<A>::value || + std::is_pointer<A>::value, + "No valid property accessor provided!"); + + impl::property_impl<ClassType, A>(name, acc, std::vector<rttr::metadata>(), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A acc, std::vector<rttr::metadata> data) +{ + static_assert(std::is_member_object_pointer<A>::value || + std::is_pointer<A>::value, + "No valid property accessor provided!"); + impl::property_impl<ClassType, A>(name, acc, std::move(data), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A, typename Policy> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A acc, const Policy& policy, + typename std::enable_if<detail::contains<Policy, detail::policy_list>::value>::type*) +{ + static_assert(std::is_member_object_pointer<A>::value || + std::is_pointer<A>::value, + "No valid property accessor provided!"); + impl::property_impl<ClassType, A>(name, acc, std::vector<rttr::metadata>(), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A, typename Policy> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A acc, std::vector<rttr::metadata> data, + const Policy& policy) +{ + static_assert(std::is_member_object_pointer<A>::value || + std::is_pointer<A>::value, + "No valid property accessor provided!"); + impl::property_impl<ClassType, A>(name, acc, std::move(data), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A1, typename A2> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A1 getter, A2 setter, + typename std::enable_if<!detail::contains<A2, detail::policy_list>::value>::type*) +{ + impl::property_impl<ClassType, A1, A2>(name, getter, setter, std::vector<rttr::metadata>(), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A1, typename A2> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A1 getter, A2 setter, std::vector<rttr::metadata> data) +{ + impl::property_impl<ClassType, A1, A2>(name, getter, setter, std::move(data), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A1, typename A2, typename Policy> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A1 getter, A2 setter, const Policy& policy) +{ + impl::property_impl<ClassType, A1, A2>(name, getter, setter, std::vector<rttr::metadata>(), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A1, typename A2, typename Policy> +class_<ClassType>& class_<ClassType>::property(const std::string& name, A1 getter, A2 setter, + std::vector<rttr::metadata> data, const Policy& policy) +{ + impl::property_impl<ClassType, A1, A2>(name, getter, setter, std::move(data), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A> +class_<ClassType>& class_<ClassType>::property_readonly(const std::string& name, A acc) +{ + impl::property_readonly_impl<ClassType, A>(name, acc, std::vector<rttr::metadata>(), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A> +class_<ClassType>& class_<ClassType>::property_readonly(const std::string& name, A acc, std::vector<rttr::metadata> data) +{ + impl::property_readonly_impl<ClassType, A>(name, acc, std::move(data), + detail::default_property_policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A, typename Policy> +class_<ClassType>& class_<ClassType>::property_readonly(const std::string& name, A acc, const Policy& policy) +{ + impl::property_readonly_impl<ClassType, A>(name, acc, std::vector<rttr::metadata>(), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename A, typename Policy> +class_<ClassType>& class_<ClassType>::property_readonly(const std::string& name, A acc, std::vector<rttr::metadata> data, + const Policy& policy) +{ + impl::property_readonly_impl<ClassType, A>(name, acc, std::move(data), + policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename F> +class_<ClassType>& class_<ClassType>::method(const std::string& name, F function) +{ + impl::method_impl<ClassType>(name, function, std::vector< rttr::metadata >(), detail::default_invoke()); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename F> +class_<ClassType>& class_<ClassType>::method(const std::string& name, F function, std::vector< rttr::metadata > data) +{ + impl::method_impl<ClassType>(name, function, std::move(data), detail::default_invoke()); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename F, typename Policy> +class_<ClassType>& class_<ClassType>::method(const std::string& name, F function, const Policy& policy) +{ + impl::method_impl<ClassType>(name, function, std::vector< rttr::metadata >(), policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename F, typename Policy> +class_<ClassType>& class_<ClassType>::method(const std::string& name, F function, std::vector< rttr::metadata > data, const Policy& policy) +{ + impl::method_impl<ClassType>(name, function, std::move(data), policy); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename ClassType> +template<typename EnumType> +class_<ClassType>& class_<ClassType>::enumeration(std::vector< std::pair< std::string, EnumType> > enum_data, std::vector<rttr::metadata> data) +{ + impl::enumeration_impl<ClassType, EnumType>(std::move(enum_data), std::move(data)); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +// register_global + +///////////////////////////////////////////////////////////////////////////////////////// + + +} // end namespace rttr + +/*! + * \brief Use this macro to automatically register your reflection information to RTTR before `main` is called. + * + * Use it in following way: +\code{.cpp} +RTTR_REGISTER +{ + rttr::method_("foo", &foo); +} +\endcode + * + * Just place the macro in global scope in a cpp file. + * + * \remark It is not possible to place the macro multiple times in one cpp file. + * + */ +#define RTTR_REGISTER \ +namespace \ +{ \ + struct _rttr__auto_register \ + { \ + _rttr__auto_register() \ + { \ + _rttr_auto_register_reflection_function(); \ + } \ + }; \ +} \ +static const _rttr__auto_register RTTR_CAT(auto_register__,__LINE__); \ +static void _rttr_auto_register_reflection_function() + +#endif // __RTTR_REGISTER_REFLECTION_IMPL_H__ diff --git a/src/rttr/impl/rttr_cast_impl.h b/src/rttr/impl/rttr_cast_impl.h new file mode 100644 index 00000000..c018da7b --- /dev/null +++ b/src/rttr/impl/rttr_cast_impl.h @@ -0,0 +1,52 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/type.h" +#include "rttr/detail/misc_type_traits.h" + +#include <type_traits> + +namespace rttr +{ + +template<typename TargetType, typename SourceType> +RTTR_INLINE TargetType rttr_cast(SourceType object) +{ + static_assert(detail::pointer_count<TargetType>::value == 1, "Return type must be a pointer"); + static_assert(detail::pointer_count<TargetType>::value == 1, "Argument type must be a pointer"); + static_assert(rttr::detail::has_get_type_func<SourceType>::value, "Class has not type defined - please use the macro RTTR_ENABLE."); + + typedef typename std::remove_pointer<TargetType>::type ReturnType; + typedef typename std::remove_pointer<SourceType>::type ArgType; + static_assert( (std::is_const<ArgType>::value && std::is_const<ReturnType>::value) || + (!std::is_const<ArgType>::value && std::is_const<ReturnType>::value) || + (!std::is_const<ArgType>::value && !std::is_const<ReturnType>::value), "Return type must have const qualifier"); + + return static_cast<TargetType>(type::apply_offset(object->get_ptr(), object->get_type(), type::get<TargetType>())); +} + +} diff --git a/src/rttr/impl/type_impl.h b/src/rttr/impl/type_impl.h new file mode 100644 index 00000000..089449a2 --- /dev/null +++ b/src/rttr/impl/type_impl.h @@ -0,0 +1,349 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TYPE_IMPL_H__ +#define __RTTR_TYPE_IMPL_H__ + +#include <type_traits> +#include "rttr/detail/misc_type_traits.h" +#include "rttr/detail/function_traits.h" + +namespace rttr +{ + +namespace detail +{ + template<typename TargetType, typename SourceType, typename F> + struct type_converter; +} + +RTTR_INLINE type::type() +: m_id(0) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type::type(type::type_id id) +: m_id(id) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type::type(const type& other) +: m_id(other.m_id) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type& type::operator=(const type& other) +{ + m_id = other.m_id; + return *this; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator<(const type& other) const +{ + return (m_id < other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator>(const type& other) const +{ + return (m_id > other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator>=(const type& other) const +{ + return (m_id >= other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator<=(const type& other) const +{ + return (m_id <= other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator==(const type& other) const +{ + return (m_id == other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::operator!=(const type& other) const +{ + return (m_id != other.m_id); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type::type_id type::get_id() const +{ + return m_id; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool type::is_valid() const +{ + return (m_id != 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type::operator bool() const +{ + return (m_id != 0); +} + + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +namespace detail +{ +class constructor_container_base; +class destructor_container_base; +class enumeration_container_base; +class method_container_base; +class property_container_base; +} + +namespace impl +{ + +RTTR_API void register_property(type, std::unique_ptr<detail::property_container_base>); +RTTR_API void register_method(type, std::unique_ptr<detail::method_container_base>); +RTTR_API void register_constructor(type, std::unique_ptr<detail::constructor_container_base>); +RTTR_API void register_destructor(type, std::unique_ptr<detail::destructor_container_base>); +RTTR_API void register_enumeration(type t, std::unique_ptr<detail::enumeration_container_base>); + +static type get_invalid_type() { return type(); } + +template <typename T> +struct MetaTypeInfo +{ + enum { Defined = 0 }; + static type get_type() + { + // when you get this error, you have to declare first the type with this macro + static_assert(sizeof(T) != sizeof(T), "The given type T is not registered to the type system; please register with RTTR_DECLARE_TYPE."); + return get_invalid_type(); + } +}; + + +template<typename T> +struct auto_register_type; +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +static RTTR_INLINE type get_type_from_instance(const T*) +{ + return impl::MetaTypeInfo<T>::get_type(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, bool = std::is_same<T, typename detail::raw_type<T>::type >::value> +struct raw_type_info +{ + static RTTR_INLINE type get_type() { return get_invalid_type(); } // we have to return an empty type, so we can stop the recursion +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +struct raw_type_info<T, false> +{ + static RTTR_INLINE type get_type() { return MetaTypeInfo<typename detail::raw_type<T>::type>::get_type(); } +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, bool> +struct type_from_instance; + +//! Specialization for retrieving the type from the instance directly +template<typename T> +struct type_from_instance<T, false> // the typeInfo function is not available +{ + static RTTR_INLINE type get(T&&) + { + return impl::MetaTypeInfo<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::get_type(); + } +}; + +//! Specialization for retrieving the type from the instance directly +template<typename T> +struct type_from_instance<T, true> +{ + static RTTR_INLINE type get(T&& object) + { + return object.get_type(); + } +}; + +} // end namespace impl + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +RTTR_INLINE type type::get() +{ + return impl::MetaTypeInfo<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::get_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +RTTR_INLINE type type::get(T&& object) +{ + using remove_ref = typename std::remove_reference<T>::type; + return impl::type_from_instance<T, detail::has_get_type_func<T>::value && !std::is_pointer<remove_ref>::value>::get(std::forward<T>(object)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +RTTR_INLINE bool type::is_derived_from() const +{ + return is_derived_from(type::get<T>()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename F> +RTTR_INLINE void type::register_converter_func(F func) +{ + using target_type_orig = typename detail::function_traits<F>::return_type; + using target_type = typename std::remove_cv<typename std::remove_reference<target_type_orig>::type>::type; + + const std::size_t arg_count = detail::function_traits<F>::arg_count; + + static_assert(arg_count == 2, "Invalid argument count! The converter function signature must be: <target_type(source_type, bool&)>"); + static_assert(!std::is_same<void, target_type>::value, "Return type cannot be void!"); + static_assert(std::is_same<bool&, typename detail::param_types<F, 1>::type>::value, "Second argument type must be a bool reference(bool&)."); + + using source_type_orig = typename detail::param_types<F, 0>::type; + using source_type = typename std::remove_cv<typename std::remove_reference<source_type_orig>::type>::type; + + std::unique_ptr<detail::type_converter_base> converter(new detail::type_converter<target_type, source_type, F>(func)); + type source_t = type::get<source_type>(); + source_t.register_type_converter(std::move(converter)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr + + +namespace std +{ +template <> +class hash<rttr::type> +{ +public: + size_t operator()(const rttr::type& info) const + { + return hash<rttr::type::type_id>()(info.get_id()); + } +}; +} // end namespace std + +static void _rttr_auto_register_reflection_function(); +#define RTTR_REGISTER_FRIEND friend void _rttr_auto_register_reflection_function(); + +#define RTTR_CAT_IMPL(a,b) a##b +#define RTTR_CAT(a,b) RTTR_CAT_IMPL(a,b) + +#define RTTR_DECLARE_TYPE(...) \ +namespace rttr \ +{ \ + namespace impl \ + { \ + template<> \ + struct MetaTypeInfo< __VA_ARGS__ > \ + { \ + enum { Defined = 1 }; \ + static RTTR_INLINE rttr::type get_type() \ + { \ + static const type val = rttr::type::register_type(#__VA_ARGS__, \ + raw_type_info<__VA_ARGS__>::get_type(), \ + std::move(base_classes<__VA_ARGS__>::get_types()), \ + get_most_derived_info_func<__VA_ARGS__>(), \ + get_create_variant_func<__VA_ARGS__>(), \ + std::is_class<__VA_ARGS__>::value, \ + std::is_enum<__VA_ARGS__>::value, \ + ::rttr::detail::is_array<__VA_ARGS__>::value, \ + std::is_pointer<__VA_ARGS__>::value, \ + std::is_arithmetic<__VA_ARGS__>::value); \ + return val; \ + } \ + }; \ + } \ +} // end namespace rttr + +#define RTTR_DEFINE_TYPE(...) \ +namespace rttr \ +{ \ + namespace impl \ + { \ + template<> \ + struct auto_register_type<__VA_ARGS__> \ + { \ + auto_register_type() \ + { \ + MetaTypeInfo<__VA_ARGS__>::get_type(); \ + } \ + }; \ + } \ +} \ +static const rttr::impl::auto_register_type<__VA_ARGS__> RTTR_CAT(autoRegisterType,__COUNTER__); + +#define RTTR_DECLARE_STANDARD_TYPE_VARIANTS(T) RTTR_DECLARE_TYPE(T) \ + RTTR_DECLARE_TYPE(T*) \ + RTTR_DECLARE_TYPE(const T*) + +#define RTTR_DEFINE_STANDARD_TYPE_VARIANTS(T) RTTR_DEFINE_TYPE(T) \ + RTTR_DEFINE_TYPE(T*) \ + RTTR_DEFINE_TYPE(const T*) + + +#endif // __RTTR_TYPE_IMPL_H__ diff --git a/src/rttr/impl/variant_array_impl.h b/src/rttr/impl/variant_array_impl.h new file mode 100644 index 00000000..3ed71285 --- /dev/null +++ b/src/rttr/impl/variant_array_impl.h @@ -0,0 +1,140 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/array_container.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" + +namespace rttr +{ + +namespace detail +{ +template<typename T> +array_container_base* create_array_container(const T& value); + +template<typename T> +array_container_base* create_array_container_moved(T&& value); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array::variant_array() +: _container(nullptr) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant_array::variant_array(const T& param) +: _container(detail::create_array_container(param)) + +{ + static_assert(detail::is_array<T>::value, "No Array type provided, please provide a specialization with rttr::detail::array_mapper<T>."); + static_assert(!detail::is_array<variant>::value, "No allowed to create a variant_array from variant."); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant_array::variant_array(T&& param, + typename std::enable_if<!std::is_same<variant_array&, T>::value >::type*, + typename std::enable_if<!std::is_const<T>::value >::type* + ) +: _container(detail::create_array_container_moved(std::move(param))) +{ + using type = typename detail::raw_type<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::type; + static_assert(detail::is_array<type>::value, "No Array type provided, please provide a specialization with rttr::detail::array_mapper<T>."); + static_assert(!detail::is_array<variant>::value, "No allowed to create a variant_array from variant."); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array::variant_array(const variant_array& other) +: _container(other._container ? other._container->clone() : nullptr) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array::variant_array(variant_array&& other) +: _container(other._container) +{ + other._container = nullptr; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array::~variant_array() +{ + delete _container; +#if RTTR_COMPILER == RTTR_COMPILER_MSVC + #if RTTR_COMP_VER <= 1800 + _container = nullptr; + #else + #error "Please check if this lead to still to a crash." + #endif +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE void variant_array::swap(variant_array& other) +{ + std::swap(_container, other._container); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +RTTR_INLINE variant_array& variant_array::operator=(T&& other) +{ + variant_array(static_cast<T&&>(other)).swap(*this); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array& variant_array::operator =(const variant_array& other) +{ + variant_array(other).swap(*this); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant_array& variant_array::operator=(variant_array&& other) +{ + other.swap(*this); + variant_array().swap(other); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/impl/variant_default_types_impl.h b/src/rttr/impl/variant_default_types_impl.h new file mode 100644 index 00000000..ee8bca09 --- /dev/null +++ b/src/rttr/impl/variant_default_types_impl.h @@ -0,0 +1,263 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/misc_type_traits.h" +#include "rttr/detail/utility.h" +#include "rttr/detail/type_converter.h" + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<void> : public variant_container_base +{ + public: + variant_container(); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<std::string> : public variant_container_base +{ + public: + variant_container(const std::string& arg); + + variant_container(std::string&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + std::string _value; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<int> : public variant_container_base +{ + public: + variant_container(const int& arg); + + variant_container(int&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + int _value; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<bool> : public variant_container_base +{ + public: + variant_container(const bool& arg); + + variant_container(bool&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + bool _value; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<float> : public variant_container_base +{ + public: + variant_container(const float& arg); + + variant_container(float&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + bool can_convert(const type& target_type) const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + float _value; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + +///////////////////////////////////////////////////////////////////////////////// + +template<> +class RTTR_API variant::variant_container<double> : public variant_container_base +{ + public: + variant_container(const double& arg); + + variant_container(double&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + bool can_convert(const type& target_type) const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + double _value; + + private: // unimplemented + variant_container & operator=(const variant_container &); +}; + + + +///////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/impl/variant_impl.h b/src/rttr/impl/variant_impl.h new file mode 100644 index 00000000..571319a8 --- /dev/null +++ b/src/rttr/impl/variant_impl.h @@ -0,0 +1,672 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/detail/misc_type_traits.h" +#include "rttr/detail/utility.h" +#include "rttr/detail/type_converter.h" +#include "rttr/detail/std_conversion_functions.h" +#include "rttr/detail/array_container.h" + +namespace rttr +{ + +namespace detail +{ +template<typename T> +detail::array_container_base* create_array_container(const T& value); +} // end namespace detail; + +RTTR_INLINE variant::variant() +: _holder(0) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant::variant(const variant& other) +: _holder(other._holder ? other._holder->clone() : nullptr) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant::variant(variant&& other) +: _holder(other._holder) +{ + other._holder = nullptr; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant::~variant() +{ + delete _holder; +#if RTTR_COMPILER == RTTR_COMPILER_MSVC + #if RTTR_COMP_VER <= 1800 + _holder = nullptr; + #else + #error "Please check if this lead to still to a crash." + #endif +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE void variant::swap(variant& other) +{ + std::swap(_holder, other._holder); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +RTTR_INLINE variant& variant::operator=(T&& other) +{ + variant(static_cast<T&&>(other)).swap(*this); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant& variant::operator =(const variant& other) +{ + variant(other).swap(*this); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant& variant::operator=(variant&& other) +{ + other.swap(*this); + variant().swap(other); + return *this; +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type variant::get_type() const +{ + return (_holder ? _holder->get_type() : impl::get_invalid_type()); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE void* variant::get_ptr() const +{ + return (_holder ? _holder->get_ptr() : nullptr); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE type variant::get_raw_type() const +{ + return (_holder ? _holder->get_raw_type() : impl::get_invalid_type()); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE void* variant::get_raw_ptr() const +{ + return (_holder ? _holder->get_raw_ptr() : nullptr); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE bool variant::is_valid() const +{ + return (_holder ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////// + +RTTR_INLINE variant::variant_container_base::~variant_container_base() +{ + +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant::variant(const T& param) +: _holder(new variant_container<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(param)) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant::variant(T&& param, + typename std::enable_if<!std::is_same<variant&, T>::value >::type*, + typename std::enable_if<!std::is_const<T>::value >::type* + ) +: _holder(new variant_container<typename std::remove_cv<typename std::remove_reference<T>::type>::type>(static_cast<T&&>(param))) +{ +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +bool variant::is_type() const +{ + if (!is_valid()) + return false; + else + return (type::get<T>() == _holder->get_type()); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +bool variant::can_convert() const +{ + if (!is_valid()) + return false; + + return _holder->can_convert(type::get<T>()); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +T& variant::get_value() const +{ + typedef typename std::remove_cv<T>::type nonRef; + return static_cast<variant_container<nonRef> *>(_holder)->_value; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +T variant::convert(bool* ok) const +{ + const type& source_type = _holder->get_type(); + const type& target_type = type::get<T>(); + if (target_type == source_type) + { + if (ok) + *ok = true; + typedef typename std::remove_cv<T>::type nonRef; + return static_cast<variant_container<nonRef> *>(_holder)->_value; + } + else + { + const auto& converter = source_type.get_type_converter(target_type); + void* data = _holder->get_ptr(); + using t_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type; + if (const auto& targert_converter = static_cast<detail::type_converter_target<t_type>*>(converter)) + { + if (ok) + { + return targert_converter->convert(data, *ok); + } + else + { + bool ok_ref = false; + return targert_converter->convert(data, ok_ref); + } + } + + if (detail::pointer_count<T>::value == 1 && source_type.is_pointer()) + { + if (void * ptr = type::apply_offset(get_raw_ptr(), source_type, target_type)) + { + if (ok) + *ok = true; + return static_cast<T>(ptr); + } + } + if (ok) + *ok = false; + + // danger the conversion doesn't worked + return static_cast<T>(nullptr); + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename SourceType> +T variant::get_value_with_default_value(const SourceType& source, T default_value, bool* ok) +{ + const type& source_type = type::get<SourceType>(); + const type& target_type = type::get<T>(); + if (const auto& converter = source_type.get_type_converter(target_type)) + { + void* data = const_cast<void*>(reinterpret_cast<const void*>(std::addressof(source))); + using t_type = typename std::remove_cv<typename std::remove_reference<T>::type>::type; + const auto& targert_converter = static_cast<detail::type_converter_target<t_type>*>(converter); + if (ok) + { + return targert_converter->convert(data, *ok); + } + else + { + bool ok_ref = false; + return targert_converter->convert(data, ok_ref); + } + } + else + { + return default_value; + } +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +variant::variant_container<T, Enable>::variant_container(const T& arg) +: _value(arg) +{ + +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +variant::variant_container<T, Enable>::variant_container(T&& arg) +: _value(std::move(arg)) +{ + +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +variant::variant_container_base* variant::variant_container<T, Enable>::clone() const +{ + return (new variant_container<T>(_value)); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +type variant::variant_container<T, Enable>::get_type() const +{ + return type::get<T>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +void* variant::variant_container<T, Enable>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +type variant::variant_container<T, Enable>::get_raw_type() const +{ + return type::get<typename detail::raw_type<T>::type>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +void* variant::variant_container<T, Enable>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +bool variant::variant_container<T, Enable>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<T>(); + if (source_type == target_type) + return true; + + if (source_type.get_type_converter(target_type)) + return true; + + if (detail::pointer_count<T>::value == 1 && target_type.is_pointer()) + { + if (void * ptr = type::apply_offset(get_raw_ptr(), source_type, target_type)) + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +bool variant::variant_container<T, Enable>::is_array() const +{ + return detail::is_array<typename detail::raw_type<T>::type>::value; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +detail::array_container_base* variant::variant_container<T, Enable>::to_array() const +{ + return detail::create_array_container(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +std::string variant::variant_container<T, Enable>::to_string(bool* ok) const +{ + return variant::get_value_with_default_value<std::string, T>(_value, std::string(), ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +int variant::variant_container<T, Enable>::to_int(bool* ok) const +{ + return variant::get_value_with_default_value<int, T>(_value, 0, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +bool variant::variant_container<T, Enable>::to_bool(bool* ok) const +{ + return variant::get_value_with_default_value<bool, T>(_value, false, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +float variant::variant_container<T, Enable>::to_float(bool* ok) const +{ + return variant::get_value_with_default_value<float, T>(_value, 0.0f, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T, typename Enable> +double variant::variant_container<T, Enable>::to_double(bool* ok) const +{ + return variant::get_value_with_default_value<double, T>(_value, 0.0, ok); +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::variant_container(const T& arg) +{ +#if RTTR_COMPILER == RTTR_COMPILER_MSVC + detail::copy_array(const_cast<std::remove_const<T>::type&>(arg), _value); +#else + detail::copy_array(arg, _value); +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +variant::variant_container_base* variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::clone() const +{ + return (new variant_container<T>(_value)); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +type variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::get_type() const +{ + return type::get<T>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +void* variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +type variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::get_raw_type() const +{ + return type::get<typename detail::raw_type<T>::type>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +void* variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +bool variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::is_array() const +{ + return detail::is_array<typename detail::raw_type<T>::type>::value; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +detail::array_container_base* variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_array() const +{ + return detail::create_array_container(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +bool variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<T>(); + if (source_type == target_type) + return true; + + if (source_type.get_type_converter(target_type)) + return true; + + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +std::string variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_string(bool* ok) const +{ + return variant::get_value_with_default_value<std::string, T>(_value, std::string(), ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +int variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_int(bool* ok) const +{ + return variant::get_value_with_default_value<int, T>(_value, 0, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +bool variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_bool(bool* ok) const +{ + return variant::get_value_with_default_value<bool, T>(_value, false, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +float variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_float(bool* ok) const +{ + return variant::get_value_with_default_value<float, T>(_value, 0.0f, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<typename T> +double variant::variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value >::type>::to_double(bool* ok) const +{ + return variant::get_value_with_default_value<double, T>(_value, 0.0, ok); +} + +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////// +// one dimensional array + + +template<std::size_t N> +variant::variant_container<char[N]>::variant_container(const char (&arg)[N]) +{ +#if RTTR_COMPILER == RTTR_COMPILER_MSVC + detail::copy_array(const_cast<std::remove_const<char[N]>::type&>(arg), _value); +#else + detail::copy_array(arg, _value); +#endif +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +variant::variant_container_base* variant::variant_container<char[N]>::clone() const +{ + return (new variant_container<char[N]>(_value)); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +type variant::variant_container<char[N]>::get_type() const +{ + return type::get<char[N]>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +void* variant::variant_container<char[N]>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +type variant::variant_container<char[N]>::get_raw_type() const +{ + return type::get<typename detail::raw_type<char[N]>::type>(); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +void* variant::variant_container<char[N]>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +bool variant::variant_container<char[N]>::is_array() const +{ + return detail::is_array<typename detail::raw_type<char[N]>::type>::value; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +detail::array_container_base* variant::variant_container<char[N]>::to_array() const +{ + return detail::create_array_container<char[N]>(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +bool variant::variant_container<char[N]>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<char[N]>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<std::string>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +std::string variant::variant_container<char[N]>::to_string(bool* ok) const +{ + if (ok) + *ok = true; + return std::string(_value); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +int variant::variant_container<char[N]>::to_int(bool* ok) const +{ + return detail::char_to_int(_value, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +bool variant::variant_container<char[N]>::to_bool(bool* ok) const +{ + return detail::char_to_bool(_value, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +float variant::variant_container<char[N]>::to_float(bool* ok) const +{ + return detail::char_to_float(_value, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +template<std::size_t N> +double variant::variant_container<char[N]>::to_double(bool* ok) const +{ + return detail::char_to_double(_value, ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/metadata.h b/src/rttr/metadata.h new file mode 100644 index 00000000..e7410d05 --- /dev/null +++ b/src/rttr/metadata.h @@ -0,0 +1,66 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METADATA_H__ +#define __RTTR_METADATA_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/variant.h" + +#include <algorithm> +#include <string> +#include <utility> + +namespace rttr +{ + +/*! + * This class is used to add custom meta data to the binding of a type. + * + * + * \see method, property, enumeration, constructor and destructor + */ +class RTTR_API metadata +{ + public: + metadata(const metadata& other) : _key(other._key), _value(other._value) {} + metadata(std::string key, variant value) : _key(std::move(key)), _value(std::move(value)) {} + metadata(int key, variant value) : _key(std::move(key)), _value(std::move(value)) {} + metadata(metadata&& data) : _key(std::move(data._key)), _value(std::move(data._value)) { data._key = variant(); data._value = variant(); } + metadata& operator=(metadata other) { std::swap(_key, other._key); std::swap(_value, other._value); return *this; } + + variant get_key() const { return _key; } + variant get_value() const { return _value; } + + private: + variant _key; + variant _value; +}; + +} // end namespace rttr + +#endif // __RTTR_METADATA_H__ diff --git a/src/rttr/method.cpp b/src/rttr/method.cpp new file mode 100644 index 00000000..bd20b5e8 --- /dev/null +++ b/src/rttr/method.cpp @@ -0,0 +1,238 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/method.h" +#include "rttr/detail/method_container_base.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +method::method(const detail::method_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool method::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method::operator bool() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string method::get_name() const +{ + if (is_valid()) + return _container->get_name(); + else + return string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool method::is_static() const +{ + if (is_valid()) + return _container->is_static(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type method::get_return_type() const +{ + if (is_valid()) + return _container->get_return_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type method::get_declaring_type() const +{ + if (is_valid()) + return _container->get_declaring_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<type> method::get_parameter_types() const +{ + if (is_valid()) + return _container->get_parameter_types(); + else + return vector<type>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string method::get_signature() const +{ + if (is_valid()) + return _container->get_signature(); + else + return string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::get_metadata(int key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::get_metadata(const std::string& key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object) const +{ + if (is_valid()) + return _container->invoke(object); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1) const +{ + if (is_valid()) + return _container->invoke(object, arg1); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1, detail::argument arg2) const +{ + if (is_valid()) + return _container->invoke(object, arg1, arg2); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3) const +{ + if (is_valid()) + return _container->invoke(object, arg1, arg2, arg3); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4) const +{ + if (is_valid()) + return _container->invoke(object, arg1, arg2, arg3, arg4); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5) const +{ + if (is_valid()) + return _container->invoke(object, arg1, arg2, arg3, arg4, arg5); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5, detail::argument arg6) const +{ + if (is_valid()) + return _container->invoke(object, arg1, arg2, arg3, arg4, arg5, arg6); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant method::invoke_variadic(detail::instance object, std::vector<detail::argument> args) const +{ + if (is_valid()) + return _container->invoke_variadic(object, args); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool method::operator==(const method& other) const +{ + return (_container == other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool method::operator!=(const method& other) const +{ + return (_container != other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr diff --git a/src/rttr/method.h b/src/rttr/method.h new file mode 100644 index 00000000..76b79027 --- /dev/null +++ b/src/rttr/method.h @@ -0,0 +1,296 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_METHOD_H__ +#define __RTTR_METHOD_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <string> +#include <vector> + +namespace rttr +{ + +class variant; +class type; + +namespace detail +{ +class method_container_base; +class instance; +class argument; +} + +/*! + * The \ref method class provides several meta information about a method and can be invoked. + * + * A instance of a method class can only be obtained from the \ref type class. + * See \ref type::get_method() and \ref type::get_methods(). + * + * For registration a method, nested inside a class, see \ref class_::method() and for global methods see \ref register_global::method. + * + * Meta Information + * ---------------- + * A \ref method has a \ref get_name "name", a \ref get_signature "signature", a \ref get_return_type "return type", a list of \ref get_parameter_types "parameter types" + * as well as attributes that specify its behavior: \ref is_static(). + * When the \ref method was declared inside a class, then \ref get_declaring_type() can be used to obtain the type of this class. + * + * The method can be invoked with \ref invoke(); When its not a \ref is_static "static method" you have to provide a valid class instance to invoke the method. + * This instance can be the raw type on the stack; the current class hierarchy level doesn't matter. It can be also a raw pointer to the object or + * a \ref variant which contains the instance, again as pointer or stack object. + * When the method is declared as static you you still have to provide a dummy instance object, therefore the function `rttr::empty_instance()` should be used. + * + * A method will be successfully invoked when the provided instance can be converted to the \ref get_declaring_type() "declared class" type. + * When the method has \ref get_parameter_types "parameters" defined, then the same number of arguments must be provided and the type itself must 100% match the type of the registered function. + * An automatically type conversion is **not** performed. + * + * The return type of \ref invoke() is \ef variant object. + * This object contains not only the possible return value of a function, it also indicates whether the method was invoked or not. + * A \ref variant::is_valid "valid" variant object means, that then the method was successfully invoked, otherwise not. + * When the invoked method has no return type, i.e. is a `void` method, then a valid variant of type `void` is returned. + * + * While the \ref invoke() function can directly forward up to six arguments, it is sometime necessary to forward even more arguments. + * Therefore the function \ref invoke_variadic() should be used; it allows to pack an unlimited amount of arguments into a std::vector and forward them to the function. + * + * Another way to invoke a method is to use the \ref type class through \ref invoke_method(). + * + * Copying and Assignment + * ---------------------- + * A \ref method object is lightweight and can be copied by value. However, each copy will refer to the same underlying method. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + using namespace rttr; + struct MyStruct { int my_method(int param) { return param; } }; + //... + variant obj = type::get("MyStruct").create(); + method func = obj.get_type().get_method("my_method"); + if (func) + { + variant val = func.invoke(obj, 23); + std::cout << val.get_value<int>(); // prints 23 + // you can also invoke the method with an object on the stack + MyStruct inst; + val = func.invoke(inst, 42); + std::cout << val.get_value<int>(); // prints 42 + // or as pointer + MyStruct* ptr = &inst; + val = func.invoke(ptr, 7); + std::cout << val.get_value<int>(); // prints 7 + } +\endcode + * + * \see property, enumeration, constructor and type + */ +class RTTR_API method +{ + public: + /*! + * \brief Returns true if this method is valid, otherwise false. + * + * \return True if this method is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this method is valid or not. + * + * \return True if this method is valid, otherwise false. + */ + operator bool() const; + + /*! + * \brief Returns the name of this method. + * + * \return Name of the method. + */ + std::string get_name() const; + + /*! + * \brief Returns true if this method is static method, otherwise false. + * A static method does not need an instance for performing an invoke. + * + * \remark When the method is not valid, this function will return false. + * + * \return True if this is a static method, otherwise false. + */ + bool is_static() const; + + /*! + * Returns the type object of the return type. + * + * \return The type of the return type. + */ + type get_return_type() const; + + /*! + * Returns the class that declares this method. + * + * \remark When this method does not belong to a class (i.e. is a global method) it will return an invalid type. + * When this method is not valid, this function will return an invalid type object (see \ref type::is_valid). + * + * \return Type of the underlying property. + */ + type get_declaring_type() const; + + /*! + * \brief Returns an ordered list of type objects which represents the parameters of this method. + * + * \return A list of type objects from the arguments. + */ + std::vector<type> get_parameter_types() const; + + /*! + * \brief Returns the signature of this method as readable string. + * + * \return The signature as readable string. + */ + std::string get_signature() const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(int key) const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(const std::string& key) const; + + /*! + * \brief Invokes the method represented by the current instance \p object. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1, detail::argument arg2) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke(detail::instance object, detail::argument arg1, detail::argument arg2, detail::argument arg3, detail::argument arg4, + detail::argument arg5, detail::argument arg6) const; + + /*! + * \brief Invokes the method represented by the current instance \p object, using the specified parameters. + * Use this method when the argument count is higher then six. + * + * \remark Using this invoke function is slower, then specifying the arguments directly. + * When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The type of the return type. + */ + variant invoke_variadic(detail::instance object, std::vector<detail::argument> args) const; + + /*! + * \brief Returns true if this method is the same like the \p other. + * + * \return True if both methods are equal, otherwise false. + */ + bool operator==(const method& other) const; + + /*! + * Returns true if this method is the not the same like the \p other. + * + * \return True if both methods are different, otherwise false. + */ + bool operator!=(const method& other) const; + + private: + friend class type; // to prevent creation of this class + method(const detail::method_container_base* container = nullptr); + private: + const detail::method_container_base* _container; +}; + +} // end namespace rttr + +#endif // __RTTR_METHOD_H__ diff --git a/src/rttr/pch.h b/src/rttr/pch.h new file mode 100644 index 00000000..32ebb784 --- /dev/null +++ b/src/rttr/pch.h @@ -0,0 +1,16 @@ +// std stuff +#include <map> +#include <string> +#include <vector> +#include <set> +#include <list> +#include <iostream> +#include <limits> +#include <sstream> +#include <fstream> +#include <iomanip> +#include <tuple> +#include <algorithm> +#include <cassert> +#include <memory> +#include <type_traits> diff --git a/src/rttr/policy.cpp b/src/rttr/policy.cpp new file mode 100644 index 00000000..d3895bf4 --- /dev/null +++ b/src/rttr/policy.cpp @@ -0,0 +1,43 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/policy.h" + +namespace rttr +{ + +namespace detail +{ + return_as_copy default_property_policy; +} // end namespace detail; + +detail::bind_property_as_ptr_policy bind_property_as_ptr; +detail::return_reference_as_ptr_policy return_reference_as_ptr; +detail::discard_return_value_policy discard_return_value; + + +} // end namespace rttr diff --git a/src/rttr/policy.h b/src/rttr/policy.h new file mode 100644 index 00000000..8d360930 --- /dev/null +++ b/src/rttr/policy.h @@ -0,0 +1,160 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_POLICY_H__ +#define __RTTR_POLICY_H__ + +#include "rttr/base/core_prerequisites.h" + +namespace rttr +{ + +namespace detail +{ + +struct return_as_ptr +{}; + +struct set_as_ptr +{}; + +struct return_as_copy +{}; + +struct read_only +{}; + +struct set_value +{}; + +struct discard_return +{}; + +struct default_invoke +{}; + +///////////////////////////////////////////////////////////////////////////////////////// + +// default getter policy +template<typename T> +struct get_getter_policy +{ + typedef return_as_copy type; +}; + +// default setter policy +template<typename T> +struct get_setter_policy +{ + typedef set_value type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct bind_property_as_ptr_policy +{}; + +struct return_reference_as_ptr_policy +{}; + +struct discard_return_value_policy +{}; + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename... T> +struct policy_list_impl {}; + +using policy_list = policy_list_impl<bind_property_as_ptr_policy, + return_reference_as_ptr_policy, + discard_return_value_policy>; + +///////////////////////////////////////////////////////////////////////////////////////// + +template<> +struct get_getter_policy<bind_property_as_ptr_policy> +{ + typedef return_as_ptr type; +}; + +template<> +struct get_setter_policy<bind_property_as_ptr_policy> +{ + typedef set_as_ptr type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +// default method policy +template<typename T> +struct get_method_policy +{ + typedef default_invoke type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + + +template<> +struct get_method_policy<return_reference_as_ptr_policy> +{ + typedef return_as_ptr type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +template<> +struct get_method_policy<discard_return_value_policy> +{ + typedef discard_return type; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_API extern return_as_copy default_property_policy; + +} // end namespace detail; + +/*! + * \brief + */ +RTTR_API extern detail::bind_property_as_ptr_policy bind_property_as_ptr; + +/*! + * \brief + */ +RTTR_API extern detail::return_reference_as_ptr_policy return_reference_as_ptr; + +/*! + * \brief + */ +RTTR_API extern detail::discard_return_value_policy discard_return_value; + + +} // end namespace rttr + +#endif // __RTTR_POLICY_H__ diff --git a/src/rttr/property.cpp b/src/rttr/property.cpp new file mode 100644 index 00000000..5d4fa766 --- /dev/null +++ b/src/rttr/property.cpp @@ -0,0 +1,197 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/property.h" + +#include "rttr/detail/property_container_base.h" +#include "rttr/variant.h" +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" +#include "rttr/enumeration.h" + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +property::property(const detail::property_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +property::operator bool() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::is_readonly() const +{ + if (is_valid()) + return _container->is_readonly(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::is_static() const +{ + if (is_valid()) + return _container->is_static(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::is_enumeration() const +{ + if (is_valid()) + return _container->get_type().is_enumeration(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration property::get_enumeration() const +{ + if (is_valid()) + return _container->get_type().get_enumeration(); + else + return impl::get_invalid_type().get_enumeration(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::is_array() const +{ + if (is_valid()) + return _container->is_array(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +string property::get_name() const +{ + if (is_valid()) + return _container->get_name(); + else + return string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type property::get_type() const +{ + if (is_valid()) + return _container->get_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type property::get_declaring_type() const +{ + if (is_valid()) + return _container->get_declaring_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::set_value(detail::instance object, detail::argument arg) const +{ + if (is_valid()) + return _container->set_value(object, arg); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant property::get_value(detail::instance object) const +{ + if (is_valid()) + return _container->get_value(object); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant property::get_metadata(int key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant property::get_metadata(const std::string& key) const +{ + if (is_valid()) + return _container->get_metadata(key); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::operator==(const property& other) const +{ + return (_container == other._container); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool property::operator!=(const property& other) const +{ + return (_container != other._container); +} + +} // end namespace rttr diff --git a/src/rttr/property.h b/src/rttr/property.h new file mode 100644 index 00000000..8c93cc32 --- /dev/null +++ b/src/rttr/property.h @@ -0,0 +1,271 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_PROPERTY_H__ +#define __RTTR_PROPERTY_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <string> + +namespace rttr +{ +class variant; +class type; +class enumeration; + +namespace detail +{ +class property_container_base; +class instance; +class argument; +} + +/*! + * The \ref property class provides several meta information about a property and gives read/write access to its value. + * + * A instance of a property class can only be obtained from the \ref type class. + * See \ref type::get_property() and \ref type::get_properties(). + * + * For registration a property, nested inside a class, see \ref class_::property_() and for global properties see \ref property_. + * + * Meta Information + * ---------------- + * A \ref property has a \ref get_name() "name", and a \ref get_type() "type" as well as attributes that specify its behavior: + * \ref is_readonly(), \ref is_static(), \ref is_enumeration(), \ref is_array(). + * When the \ref property was declared inside a class, then \ref get_declaring_type() can be used to obtain the type of this class. + * + * The property's values are set and retrieved with \ref set_value() and \ref get_value(); + * When its not a \ref is_static "static property" you have to provide a class instance to set/get the property value. + * This instance can be the raw type on the stack; the current hierarchy level doesn't matter. It can be also a raw pointer to the object or + * a \ref variant which contains the instance, again as pointer or stack object. + * When the property is declared as \ref is_static "static" you you still have to provide a dummy instance object, therefore the function `rttr::empty_instance()` should be used. + * + * A property will be successfully \ref set_value "set" when the provided instance can be converted to the \ref get_declaring_type() "declared class" type. + * The new forwarded property value must 100% match the type of the registered property. An automatically type conversion is **not** performed. + * + * The return type of \ref get_value() is \ref variant object. + * This object contains not only the value of the property, it also indicates whether the property value could be retrieved or not. + * A \ref variant::is_valid "valid" variant object means, that the property was successfully retrieved, otherwise not. + * + * Another way to get access a property is through \ref type "type's" set and get functions. See \ref type::set_property_value() and type::get_property_value() for details. + * + * Copying and Assignment + * ---------------------- + * A \ref property object is lightweight and can be copied by value. However, each copy will refer to the same underlying property. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + using namespace rttr; + struct MyStruct { int value = 23; }; + //... + variant obj = type::get("MyStruct").create({}); + property prop = type::get("MyStruct").get_property("value"); + if (prop) + { + variant val = prop.get_value(obj); + std::cout << val.get_value<int>(); // prints 23 + MyStruct inst; + val = prop.set_value(inst, 42); + std::cout << inst.value; // prints 42 + // or as pointer + MyStruct* ptr = &inst; + val = prop.set_value(ptr, 7); + std::cout << ptr->value; // prints 7 + // or do it all in one call + type::get(inst).set_propert_value("value", inst, 1024); + std::cout << inst.value; // prints 1024 + } +\endcode + * + * \see method, enumeration, constructor and type + */ +class RTTR_API property +{ + public: + /*! + * \brief Returns true if this property is valid, otherwise false. + * + * \return True if this property is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this property is valid or not. + * + * \return True if this property is valid, otherwise false. + */ + operator bool() const; + + /*! + * \brief Returns true if this property is read only, otherwise false. + * + * \remark When the property is not valid, this function will return false. + * + * \return True if this is a read only property, otherwise false. + */ + bool is_readonly() const; + + + /*! + * \brief Returns true if this property is static property, otherwise false. + * A static property does not need an instance for performing set_value/get_value. + * + * \remark When the property is not valid, this function will return false. + * + * \return True if this is a static property, otherwise false. + */ + bool is_static() const; + + + /*! + * \brief Returns true if the underlying property is an \ref enumeration. + * + * \remark When the property is not valid, this function will return false. + * + * \return True if this is a \ref enumeration type, otherwise false. + */ + bool is_enumeration() const; + + /*! + * \brief Returns the enumerator if this property is an enum type; + * otherwise the returned value is \ref enumeration::is_valid "not valid". + * + * \see is_enumeration() + * + * \return An enumeration object. + */ + enumeration get_enumeration() const; + + /*! + * \brief Returns true if the underlying property is an \ref array. + * + * \remark When the property is not valid, this function will return false. + * + * \return True if this is a \ref array type, otherwise false. + */ + bool is_array() const; + + /*! + * \brief Returns the name of this property. + * + * \remark When the property is not valid, this function will return an empty string. + * + * \return Name of the property. + */ + std::string get_name() const; + + /*! + * \brief Returns the underlying \ref type object of this property. + * + * \remark When the property is not valid, this function will return an invalid type object. + * + * \return \ref type "Type" of the underlying property. + */ + type get_type() const; + + /*! + * \brief Returns the \ref type of the class or struct that declares this property. + * + * \remark When this property does not belong to a class (i.e. is a global property) it will return an invalid type object. + * When this property is not valid, this function will return an invalid type object (see \ref type::is_valid). + * + * \return \ref type "Type" of the declaring class/struct for this property. + */ + type get_declaring_type() const; + + /*! + * \brief Set the property of the given instance \p object to the given value \p arg. + * + * \remark When the property is declared as \ref is_readonly "read only" this function will return false. + * When you have a static property just pass an empty instance as object argument. + * When the property is not valid, this function will return false. + * + * \see get_value(). + * + * \return The return value indicates whether the operation was successful or not. + */ + bool set_value(detail::instance object, detail::argument arg) const; + + /*! + * \brief Returns the current property value of the given instance \p object. + * + * \remark When the property is static, you can forward an empty instance. + * + * \see set_value(). + * + * \return The property value of the given instance \p object. + */ + variant get_value(detail::instance object) const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(int key) const; + + /*! + * \brief Returns the metadata for the given key \p key. + * + * \remark When no metadata is registered with the given \p key, + * an invalid \ref variant object is returned (see \ref variant::is_valid). + * + * \return A variant object, containing arbitrary data. + */ + variant get_metadata(const std::string& key) const; + + /*! + * \brief Returns true if this property is the same like the \p other. + * + * \return True if both properties are equal, otherwise false. + */ + bool operator==(const property& other) const; + + /*! + * Returns true if this property is the not the same like the \p other. + * + * \return True if both properties are different, otherwise false. + */ + bool operator!=(const property& other) const; + + private: + friend class type; // to prevent creation of this class + //! Constructs a property from a property_container_base. + property(const detail::property_container_base* container = nullptr); + private: + const detail::property_container_base* _container; +}; + +} // end namespace rttr + +#endif // __RTTR_PROPERTY_H__ diff --git a/src/rttr/reflect b/src/rttr/reflect new file mode 100644 index 00000000..3cd5c5f4 --- /dev/null +++ b/src/rttr/reflect @@ -0,0 +1,45 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_REFLECT_HEADERS_H__ +#define __RTTR_REFLECT_HEADERS_H__ + +#include "rttr_enable.h" +#include "rttr_cast.h" +#include "constructor.h" +#include "destructor.h" +#include "method.h" +#include "property.h" +#include "detail/array_container.h" +#include "variant.h" +#include "variant_array.h" +#include "detail/type_converter.h" +#include "type.h" +#include "register_reflection.h" +#include "detail/standard_types.h" + +#endif //__RTTR_REFLECT_HEADERS_H__ diff --git a/src/rttr/register_reflection.h b/src/rttr/register_reflection.h new file mode 100644 index 00000000..a5f8010a --- /dev/null +++ b/src/rttr/register_reflection.h @@ -0,0 +1,725 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_REGISTER_REFLECTION_H__ +#define __RTTR_REGISTER_REFLECTION_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/metadata.h" +#include "rttr/policy.h" + +#include <string> +#include <vector> + +namespace rttr +{ + +/*! + * \brief This class is used to register the constructors, properties, methods and enumeration + * for a certain class \p ClassType. + * + * Following example show the typical usage: + * + * First the class declaration. +\code{.cpp} +#include <rttr/type> +struct Mesh +{ + Mesh(); + Mesh(const std::string& name) + Vector3d getPosition() const; + void setPosition(const Vector3d& pos); + enum E_TransformSpace + { + TS_LOCAL, + TS_PARENT, + TS_WORLD + }; + + void setDirection(const Vector3 &vec, E_TransformSpace ts = TS_LOCAL); + + private: + Vector3d _pos; +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(Mesh) // to register the type +\endcode +* +* The in a cpp file, register the constructors, properties and methods of the class \p Mesh. +* +\code{.cpp} +#include "Mesh.cpp" +#include <rttr/reflect> +using namespace rttr; + +// register the class Mesh before main is called +RTTR_REFLECT +{ + class_<Mesh>() + .constructor<>() + .constructor<const string&>() + .enumeration<E_TransformSpace>({{"TS_LOCAL", TS_LOCAL}, + {"TS_PARENT", TS_PARENT}, + {"TS_WORLD", TS_WORLD}}) + .property("pos", &Mesh::getPosition, &Mesh::setPosition) + .method("setDirection", &Mesh::setDirection); +} +\endcode + * + * Use it then for example like this: + \code{.cpp} +int main() +{ + Mesh obj; + const type mesh_type = type::get(obj); + std::cout << mesh_type.get_name() << std::endl; // prints "Mesh" + + // sets/gets a property + property pos = mesh_type.get_property("pos"); + pos.set_value(obj, Vector3d(1,2,3)); // here we set the value + variant var_pos = pos.get_value(obj); // here we get the value + + Vector3d& vec = var_post.get_value<Vector3d>(); // vec == Vector3d(1,2,3) + + // invoke a method + method meth_dir = mesh_type.get_method("setDirection"); + meth_dir.invoke(obj, Vector3d(1,2,3), Mesh::TS_WORLD); + + // retrieve all properties + for (const auto& prop : mesh_type.get_properties()) + { + std::cout << prop.get_name(); // prints all property names of the type Mesh + } + + for (const auto& meth : mesh_type.get_methods()) + { + std::cout << meth.get_name(); // prints all method names of the type Mesh + } + + return 0; +} + \endcode + * + */ +template<typename ClassType> +class class_ +{ + public: + class_(std::vector< rttr::metadata > data = std::vector< rttr::metadata >()); + ~class_(); + + /*! + * \brief Register a constructor for this class type with with or without arguments. + * + * \see rttr::type::get_constructor, rttr::type::create + * + * \return Reference to this, in order to chain other calls. + */ + template<typename... Args> + class_& constructor(std::vector< rttr::metadata > data = std::vector< rttr::metadata >()); + + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member or a pointer to a variable. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A> + class_& property(const std::string& name, A acc); + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member or a pointer to a variable. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A> + class_& property(const std::string& name, A acc, std::vector< rttr::metadata > data); + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member or a pointer to a variable. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A, typename Policy> + class_& property(const std::string& name, A acc, const Policy& policy, +#ifndef DOXYGEN + typename std::enable_if<detail::contains<Policy, detail::policy_list>::value>::type* = nullptr +#endif + ); + + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member or a pointer to a variable. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A, typename Policy> + class_& property(const std::string& name, A acc, std::vector< rttr::metadata > data, const Policy& policy); + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A1, typename A2> + class_& property(const std::string& name, A1 getter, A2 setter, +#ifndef DOXYGEN + typename std::enable_if<!detail::contains<A2, detail::policy_list>::value>::type* = nullptr +#endif +); + + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A1, typename A2> + class_& property(const std::string& name, A1 getter, A2 setter, std::vector< rttr::metadata > data); + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A1, typename A2, typename Policy> + class_& property(const std::string& name, A1 getter, A2 setter, const Policy& policy); + + /*! + * \brief Register a property with read and write access to this class. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a member function, + * a pointer to a function or a std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A1, typename A2, typename Policy> + class_& property(const std::string& name, A1 getter, A2 setter, std::vector< rttr::metadata > data, const Policy& policy); + + + + /*! + * \brief Register a property with read only access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member, a pointer to a variable, + * a pointer to a member function, a pointer to a function or a std::function. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A> + class_& property_readonly(const std::string& name, A accessor); + + /*! + * \brief Register a property with read only access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member, a pointer to a variable, + * a pointer to a member function, a pointer to a function or a std::function. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A> + class_& property_readonly(const std::string& name, A acc, std::vector< rttr::metadata > data); + + /*! + * \brief Register a property with read only access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member, a pointer to a variable, + * a pointer to a member function, a pointer to a function or a std::function. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A, typename Policy> + class_& property_readonly(const std::string& name, A acc, const Policy& policy); + + /*! + * \brief Register a property with read only access to this class. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a member, a pointer to a variable, + * a pointer to a member function, a pointer to a function or a std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename A, typename Policy> + class_& property_readonly(const std::string& name, A acc, std::vector< rttr::metadata > data, const Policy& policy); + + ///////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Register a method to this class. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or an std::function. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename F> + class_& method(const std::string& name, F function); + + /*! + * \brief Register a method to this class. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or an std::function. + * \param data Additional meta data. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename F> + class_& method(const std::string& name, F function, std::vector< rttr::metadata > data); + + /*! + * \brief Register a method to this class. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or an std::function. + * \param policy The policies parameter describes how the method should be binded to the type system. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename F, typename Policy> + class_& method(const std::string& name, F function, const Policy& policy); + + /*! + * \brief Register a method to this class. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or an std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the method should be binded to the type system. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename F, typename Policy> + class_& method(const std::string& name, F function, std::vector< rttr::metadata > data, const Policy& policy); + + /*! + * \brief Register a nested enumeration of type \p EnumType + * + * \param enum_data The name of the property. + * \param data Additional meta data. + * + * \remark Before using this make sure you have registered the type with \ref #RTTR_DECLARE_TYPE. + * + * \return Reference to this, in order to chain other calls. + */ + template<typename EnumType> + class_& enumeration(std::vector< std::pair< std::string, EnumType> > enum_data, + std::vector< rttr::metadata > data = std::vector< rttr::metadata >()); + + private: + class_(const class_& other); + class_& operator=(const class_& other); +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +/*! + * \brief Register a constructor for a type T. + * + * \param data Additional meta data. + * + * \see rttr::type::get_constructor, rttr::type::create + * + * \return Reference to this, in order to chain other calls. + */ +template<typename T> +void constructor_(std::vector< rttr::metadata > data = std::vector< rttr::metadata >()); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param acc The accessor to the property; a pointer to a variable. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A> +void property_(const std::string& name, A acc); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param acc The accessor to the property; a pointer to a variable. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A> +void property_(const std::string& name, A acc, std::vector< rttr::metadata > data); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param acc The accessor to the property; a pointer to a variable. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A, typename Policy> +void property_(const std::string& name, A acc, const Policy& policy, +#ifndef DOXYGEN + typename std::enable_if<detail::contains<Policy, detail::policy_list>::value>::type* = nullptr +#endif +); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param acc The accessor to the property; a pointer to a variable. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A, typename Policy> +void property_(const std::string& name, A acc, std::vector< rttr::metadata > data, const Policy& policy); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a function or a std::function. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A1, typename A2> +void property_(const std::string& name, A1 getter, A2 setter, +#ifndef DOXYGEN + typename std::enable_if<!detail::contains<A2, detail::policy_list>::value>::type* = nullptr +#endif + ); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a function or a std::function. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A1, typename A2> +void property_(const std::string& name, A1 getter, A2 setter, std::vector< rttr::metadata > data); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a function or a std::function. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A1, typename A2, typename Policy> +void property_(const std::string& name, A1 getter, A2 setter, const Policy& policy); + +/*! + * \brief Register a property with read and write access. + * + * \param name The name of the property. + * \param getter The getter accessor to the property; this can be a pointer to a function or a std::function. + * \param setter The setter accessor to the property; this can be a pointer to a function or a std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for this class, otherwise it will not be registered. + */ +template<typename A1, typename A2, typename Policy> +void property_(const std::string& name, A1 getter, A2 setter, +#ifndef DOXYGEN + std::vector< rttr::metadata > data, const Policy& policy +#endif +); + +/*! + * \brief Register a property with read only access. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a variable, + * a pointer to a function or a std::function. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for a namespace, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename A> +void property_readonly_(const std::string& name, A acc); + +/*! + * \brief Register a property with read only access. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a variable, + * a pointer to a function or a std::function. + * \param data Additional meta data. + * + * \remark The name of the property has to be unique for a namespace, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename A> +void property_readonly_(const std::string& name, A acc, std::vector< rttr::metadata > data); + +/*! + * \brief Register a property with read only access. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a variable, + * a pointer to a function or a std::function. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for a namespace, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename A, typename Policy> +void property_readonly_(const std::string& name, A acc, const Policy& policy); + +/*! + * \brief Register a property with read only access. + * + * \param name The name of the property. + * \param acc The accessor to the property; this can be a pointer to a variable, + * a pointer to a function or a std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the property should be binded to the type system. + * + * \remark The name of the property has to be unique for a namespace, otherwise it will not be registered. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename A, typename Policy> +void property_readonly_(const std::string& name, A acc, std::vector< rttr::metadata > data, const Policy& policy); + +/*! + * \brief Register a free function. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or a std::function. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename F> +void method_(const std::string& name, F function); + +/*! + * \brief Register a free function. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or a std::function. + * \param data Additional meta data. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename F> +void method_(const std::string& name, F function, std::vector< rttr::metadata > data); + +/*! + * \brief Register a free function. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or a std::function. + * \param policy The policies parameter describes how the method should be binded to the type system. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename F, typename Policy> +void method_(const std::string& name, F function, const Policy& policy); + +/*! + * \brief Register a free function. + * + * \param name The name of the method. + * \param function The function accessor to this method; this can be a member function, a function or a std::function. + * \param data Additional meta data. + * \param policy The policies parameter describes how the method should be binded to the type system. + * + * \remark The method name does not have to be unique for this class. + * + * \return Reference to this, in order to chain other calls. + */ +template<typename F, typename Policy> +void method_(const std::string& name, F function, std::vector< rttr::metadata > data, const Policy& policy); + +/*! + * \brief Register an enumeration of type \p EnumType + * + * \param enum_data The name of the property. + * \param data Additional meta data. + * + * \remark Before using this make sure you have registered the type with \ref #RTTR_DECLARE_TYPE. + */ +template<typename EnumType> +void enumeration_(std::vector< std::pair< std::string, EnumType> > enum_data, + std::vector< rttr::metadata > data = std::vector< rttr::metadata >()); + + + +#ifdef DOXYGEN +/*! + * \brief Use this macro to automatically register your reflection information to RTTR before `main` is called. + * + * Use it in following way: +\code{.cpp} +RTTR_REGISTER +{ + rttr::method_("foo", &foo); +} +\endcode + * + * Just place the macro in global scope in a cpp file. + * + * \remark It is not possible to place the macro multiple times in one cpp file. + * + */ +#define RTTR_REGISTER + +/*! + * \brief Place this macro inside a class, when you need to reflect properties or methods + * which are declared in private scope of the class. + * +\code{.cpp} +class Foo +{ + private: + int m_value; + + RTTR_REGISTER_FRIEND +}; +\endcode + +\code{.cpp} +RTTR_REGISTER +{ + class_<Foo>() + .property("value", &Foo::m_value); +} +\endcode + */ +#define RTTR_REGISTER_FRIEND +#endif + +} // end namespace rttr + +#include "rttr/impl/register_reflection_impl.h" + +#endif //__RTTR_REGISTER_REFLECTION_H__ diff --git a/src/rttr/rttr.cmake b/src/rttr/rttr.cmake new file mode 100644 index 00000000..84ee1206 --- /dev/null +++ b/src/rttr/rttr.cmake @@ -0,0 +1,103 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +set(SOURCE_FILES constructor.cpp + destructor.cpp + enumeration.cpp + method.cpp + property.cpp + policy.cpp + type.cpp + variant.cpp + variant_array.cpp + detail/constructor_container_base.cpp + detail/destructor_container_base.cpp + detail/enumeration_container_base.cpp + detail/method_container_base.cpp + detail/property_container_base.cpp + detail/metadata_container.cpp + detail/standard_types.cpp + detail/reflection_database.cpp + detail/standard_types_char.cpp + detail/std_conversion_functions.cpp + ) + +set(HEADER_FILES constructor.h + destructor.h + enumeration.h + metadata.h + method.h + property.h + rttr_cast.h + rttr_enable.h + type.h + variant.h + variant_array.h + register_reflection.h + policy.h + type + reflect + base/core_prerequisites.h + base/version.h.in + impl/register_reflection_impl.h + impl/rttr_cast_impl.h + impl/type_impl.h + impl/variant_impl.h + impl/variant_default_types_impl.h + impl/variant_array_impl.h + detail/argument.h + detail/array_mapper.h + detail/array_container.h + detail/array_container_base.h + detail/array_accessor.h + detail/accessor_type.h + detail/instance.h + detail/constructor_container.h + detail/constructor_container_base.h + detail/destructor_container.h + detail/destructor_container_base.h + detail/method_accessor.h + detail/method_container_base.h + detail/method_container.h + detail/metadata_container.h + detail/standard_types.h + detail/function_traits.h + detail/enumeration_container_base.h + detail/enumeration_container.h + detail/property_accessor.h + detail/property_container_base.h + detail/property_container.h + detail/property_container_member_func.h + detail/property_container_func.h + detail/property_container_member_object.h + detail/property_container_object.h + detail/misc_type_traits.h + detail/utility.h + detail/reflection_database_p.h + detail/std_conversion_functions.h + detail/type_converter.h + ) \ No newline at end of file diff --git a/src/rttr/rttr_cast.h b/src/rttr/rttr_cast.h new file mode 100644 index 00000000..aed7f42d --- /dev/null +++ b/src/rttr/rttr_cast.h @@ -0,0 +1,50 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_RTTR_CAST_H__ +#define __RTTR_RTTR_CAST_H__ + +namespace rttr +{ +/*! + * \brief Casts the given \p object of type \p SourceType to an object of type \p TargetType. + * + * When the given the given \a object is an instance of type \p TargetType, then this function will cast the pointer to the \p TargetType; + * otherwise it will return a nullptr. If object is already a nullptr then it will also return a nullptr. + * + * \remark \p SourceType and \p TargetType must be both pointer types. + * + * \return A pointer of type \p TargetType + */ +template<typename TargetType, typename SourceType> +TargetType rttr_cast(SourceType object); + +} + +#include "rttr/impl/rttr_cast_impl.h" + +#endif // __RTTR_RTTR_CAST_H__ diff --git a/src/rttr/rttr_enable.h b/src/rttr/rttr_enable.h new file mode 100644 index 00000000..7a888c56 --- /dev/null +++ b/src/rttr/rttr_enable.h @@ -0,0 +1,297 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_RTTR_ENABLE_H__ +#define __RTTR_RTTR_ENABLE_H__ + +#include <type_traits> + +#include "rttr/detail/misc_type_traits.h" +#include "rttr/type.h" +#include "rttr/variant.h" + +namespace rttr +{ + +namespace detail +{ + /*! + * \brief A helper struct, which contains data about a class type. + * This is used for the casting mechanism. + */ + struct derived_info + { + void* _ptr; //!< A void pointer, which contains the address to an arbitrary instance. + type _type; //!< The corresponding typ object to the \ref _ptr + }; + + struct base_class_info + { + base_class_info(type t,void*(*rttr_cast_func)(void*)) + : _base_type(t), _rttr_cast_func(rttr_cast_func) + {} + type _base_type; + void* (*_rttr_cast_func)(void*); + }; +} + +namespace impl +{ + +//! A simple type_list +template<typename... U> struct type_list {}; + +///////////////////////////////////////////////////////////////////////////////////// +/*! + * This trait checks if a given type T has a typedef named \a base_class_list. + * has_base_class_list_impl::value is true, when it has this type, otherwise false. + */ +template <typename T> +class has_base_class_list_impl +{ + typedef char YesType[1]; + typedef char NoType[2]; + + template <typename C> + static YesType& test(typename C::base_class_list*); + + template <typename> + static NoType& test(...); + +public: + static const bool value = (sizeof(YesType) == sizeof(test<T>(0))); +}; + +/*! + * If T has a typedef called \a 'base_class_list' then inherits from true_type, otherwise inherits from false_type. + */ +template<typename T, typename = void> +struct has_base_class_list : std::integral_constant<bool, false> +{}; + +template<typename T> +struct has_base_class_list<T, typename std::enable_if<has_base_class_list_impl<T>::value>::type > : std::integral_constant<bool, true> +{}; + +typedef std::vector<detail::base_class_info> info_container; + +/*! + * This class fills from a given type_list the corresponding type objects into a std::vector. + */ +template<typename DerivedClass, typename... T> +struct type_from_base_classes; + +template<typename DerivedClass> +struct type_from_base_classes<DerivedClass> +{ + RTTR_INLINE static void fill(info_container&) + { + } +}; + +/*! + * \brief This is the trick to make the replacement of `dynamic_cast` possible. + * For every \p DerivedType is a function pointer stored, which performs a + * static_cast operation to it's BaseType. + * The given \p ptr is a void* ptr and has to be of type \p DerivedType, + * otherwise we would get undefined behavior. + * Therefore the \ref get_derived_info() function is used. It will return this information. + */ +template<typename DerivedType, typename BaseType> +static void* rttr_cast_impl(void* ptr) +{ + return static_cast<void*>(static_cast<BaseType*>(static_cast<DerivedType*>(ptr))); +} + +template<typename DerivedClass, typename BaseClass, typename... U> +struct type_from_base_classes<DerivedClass, BaseClass, U...> +{ + RTTR_INLINE static void fill(info_container& vec) + { + static_assert(has_base_class_list<BaseClass>::value, "The parent class has no base class list defined - please use the macro RTTR_ENABLE"); + vec.emplace_back(type::get<BaseClass>(), &rttr_cast_impl<DerivedClass, BaseClass>); + // retrieve also the types of all base classes of the base classes; you will get an compile error here, + // when the base class has not defined the 'base_class_list' typedef + type_from_base_classes<DerivedClass, typename BaseClass::base_class_list>::fill(vec); + // continue with the rest + type_from_base_classes<DerivedClass, U...>::fill(vec); + } +}; + +template<typename DerivedClass, class... BaseClassList, template<class...> class ClassContainer> +struct type_from_base_classes<DerivedClass, ClassContainer<BaseClassList...>> : type_from_base_classes<DerivedClass, BaseClassList...> { }; + +/*! + * This helper trait returns a vector with type object of all base classes. + * When there is no type_list defined or the class has no base class, an empty vector is returned. + */ +template<typename ClassType> +struct base_classes +{ + private: + // extract the info + RTTR_INLINE static void get_types_impl(info_container& vec, std::true_type) + { + type_from_base_classes<ClassType, typename ClassType::base_class_list>::fill(vec); + } + + // no type list defined + RTTR_INLINE static void get_types_impl(info_container&, std::false_type) + { + } + public: + RTTR_INLINE static info_container get_types() + { + info_container result; + get_types_impl(result, typename has_base_class_list<ClassType>::type()); + return result; + } +}; + +/*! + * Returns for a given type T, which is not a pointer, the address to it. + */ +template<typename T> +static void* get_ptr(const T& data, typename std::enable_if<!std::is_pointer<T>::value>::type* = 0) +{ + return const_cast<void*>(reinterpret_cast<const void*>(&data)); +} + +/*! + * Returns for a given type T, which is not a pointer, the address to it. + */ +template<typename T> +static void* get_ptr(T& data, typename std::enable_if<!std::is_pointer<T>::value>::type* = 0) +{ + return reinterpret_cast<void*>(&data); +} + +/*! + * Returns for a given type T, which a pointer, the address of the pointed data. + */ +template<typename T> +static void* get_ptr(const T& data, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) +{ + return const_cast<void*>(reinterpret_cast<const void*>(data)); +} + +/*! + * Returns for a given type T, which a pointer, the address of the pointed data. + */ +template<typename T> +static void* get_ptr(T& data, typename std::enable_if<std::is_pointer<T>::value>::type* = 0) +{ + return reinterpret_cast<void*>(data); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +template<typename T> +static detail::derived_info get_most_derived_info_impl(void* ptr) +{ + return (static_cast<T*>(ptr)->get_derived_info()); +} + +template<typename T> +static detail::derived_info get_most_derived_info_impl_none(void* ptr) +{ + return {ptr, type::get<T>()}; +} + +typedef detail::derived_info(*derived_func)(void*); + +/*! + * This is the case where the typ T has put necessary macro `RTTR_ENABLE` inside the class. + */ + +template<typename T> +static derived_func get_most_derived_info_check(typename std::enable_if<detail::has_get_derived_info_func<T>::value >::type* = 0) +{ + return get_most_derived_info_impl<T>; +} + +/*! + * This is the case where the typ T has not put necessary macro `RTTR_ENABLE` inside the class. + */ +template<typename T> +static derived_func get_most_derived_info_check(typename std::enable_if<!detail::has_get_derived_info_func<T>::value >::type* = 0) +{ + return get_most_derived_info_impl_none<T>; +} + +/*! + * \brief This function returns a function pointer to a function for retrieving the infos + * about the most derived type of an given instance. + * + */ +template<typename T> +static derived_func get_most_derived_info_func() +{ + return get_most_derived_info_check<typename detail::raw_type<T>::type>(); +} + +template<typename source_type> +static variant create_variant(void* ptr) +{ + return static_cast<source_type>(ptr); +} + +template<typename source_type> +static variant create_invalid_variant(void*) +{ + return variant(); +} + +typedef variant(*create_variant_func)(void*); + +template<typename T> +create_variant_func get_create_variant_func(typename std::enable_if< detail::pointer_count<T>::value == 1 >::type* = 0) +{ + return create_variant<T>; +} + +template<typename T> +create_variant_func get_create_variant_func(typename std::enable_if< detail::pointer_count<T>::value != 1>::type* = 0) +{ + return create_invalid_variant<T>; +} + +} // end namespace impl +} // end namespace rttr + +#define TYPE_LIST(...) rttr::impl::type_list<__VA_ARGS__> + +#define RTTR_ENABLE(...) \ +public:\ + virtual RTTR_INLINE rttr::type get_type() const { return rttr::impl::get_type_from_instance(this); } \ + virtual RTTR_INLINE void* get_ptr() { return reinterpret_cast<void*>(this); } \ + virtual RTTR_INLINE rttr::detail::derived_info get_derived_info() { return {reinterpret_cast<void*>(this), rttr::impl::get_type_from_instance(this)}; } \ + typedef TYPE_LIST(__VA_ARGS__) base_class_list; \ +private: + + +#endif // __RTTR_RTTR_ENABLE_H__ diff --git a/src/rttr/type b/src/rttr/type new file mode 100644 index 00000000..0f14ed0d --- /dev/null +++ b/src/rttr/type @@ -0,0 +1,47 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TYPE_HEADERS_H__ +#define __RTTR_TYPE_HEADERS_H__ + +#include "rttr_enable.h" +#include "rttr_cast.h" +#include "constructor.h" +#include "destructor.h" +#include "method.h" +#include "property.h" +#include "enumeration.h" +#include "detail/array_container.h" +#include "variant.h" +#include "variant_array.h" +#include "detail/type_converter.h" +#include "type.h" +#include "detail/argument.h" +#include "detail/instance.h" +#include "detail/standard_types.h" + +#endif //__RTTR_TYPE_HEADERS_H__ diff --git a/src/rttr/type.cpp b/src/rttr/type.cpp new file mode 100644 index 00000000..89be31ac --- /dev/null +++ b/src/rttr/type.cpp @@ -0,0 +1,857 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/type.h" + +#include "rttr/constructor.h" +#include "rttr/property.h" +#include "rttr/destructor.h" +#include "rttr/enumeration.h" +#include "rttr/method.h" + +#include "rttr/detail/constructor_container_base.h" +#include "rttr/detail/destructor_container_base.h" +#include "rttr/detail/enumeration_container_base.h" +#include "rttr/detail/method_container_base.h" +#include "rttr/detail/property_container.h" +#include "rttr/detail/array_container_base.h" +#include "rttr/rttr_enable.h" + +#include "rttr/detail/reflection_database_p.h" + +#include <unordered_map> +#include <vector> +#include <memory> +#include <set> +#include <thread> +#include <mutex> + +using namespace std; + +namespace rttr +{ +static detail::reflection_database::NameToTag *g_name_to_id = nullptr; +static string *g_name_list = nullptr; +static type::type_id *g_base_class_list = nullptr; +static type::type_id *g_derived_class_list = nullptr; +static detail::reflection_database::rttr_cast_func *g_conversion_list = nullptr; +static detail::reflection_database::get_derived_info_func *g_get_derived_info_func_list = nullptr; +static detail::reflection_database::variant_create_func *g_variant_create_list = nullptr; +static type::type_id *g_raw_type_list = nullptr; +static bool *g_is_class_list = nullptr; +static bool *g_is_enum_list = nullptr; +static bool *g_is_array_list = nullptr; +static bool *g_is_pointer_list = nullptr; +static bool *g_is_primitive_list = nullptr; +static unique_ptr<detail::constructor_container_base> *g_ctor_list = nullptr; +static unique_ptr<detail::destructor_container_base> *g_dtor_list = nullptr; +static unique_ptr<detail::enumeration_container_base> *g_enumeration_list = nullptr; +static std::vector<std::unique_ptr<detail::type_converter_base>> *g_type_converter_list = nullptr; +static unique_ptr<detail::reflection_database::class_data> *g_class_data_list = nullptr; +static detail::reflection_database::property_map *g_global_properties = nullptr; +static detail::reflection_database::method_map *g_global_methods = nullptr; + +static detail::reflection_database::constructor_container *g_constructor_list = nullptr; +static detail::reflection_database::destructor_container *g_destructor_list = nullptr; +static detail::reflection_database::method_container *g_method_list = nullptr; +static detail::reflection_database::property_container *g_property_list = nullptr; + + +static std::mutex register_type_mutex; + +// because everything is initialized at static initialization time the call to +// register_type can be made from another translation unit before global statics +// like 'g_name_list' are initialized, therefore we make a small singleton called type_data +// +// Before any other static function of type is called, a call to register has to be made first, +// otherwise a compile time error occurs. +static void init_globals() +{ + static bool initialized = false; + if (initialized) + return; + + detail::reflection_database& db = detail::reflection_database::instance(); + + g_name_to_id = &db.name_to_id; + g_name_list = db.name_list; + g_base_class_list = db.base_class_list; + g_derived_class_list = db.derived_class_list; + g_conversion_list = db.conversion_list; + g_variant_create_list = db.variant_create_func_list; + g_get_derived_info_func_list= db.get_derived_info_func_list; + g_raw_type_list = db.raw_type_list; + g_is_class_list = db.is_class_list; + g_is_enum_list = db.is_enum_list; + g_is_array_list = db.is_array_list; + g_is_pointer_list = db.is_pointer_list; + g_is_primitive_list = db.is_primitive_list; + g_class_data_list = db.class_data_list; + g_ctor_list = db.constructor_list; + g_dtor_list = db.destructor_list; + g_enumeration_list = db.enumeration_list; + g_global_properties = &db.global_properties; + g_global_methods = &db.global_methods; + g_constructor_list = &db._constructor_list; + g_destructor_list = &db._destructor_list; + g_method_list = &db._method_list; + g_property_list = &db._property_list; + g_type_converter_list = db.type_converter_list; + + initialized = true; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::string type::get_name() const +{ + if (is_valid()) + return g_name_list[m_id]; + else + return std::string(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type type::get_raw_type() const +{ + if (is_valid()) + return type(g_raw_type_list[m_id]); + else + return type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_derived_from(const type& other) const +{ + const type::type_id source_raw_id = g_raw_type_list[m_id]; + const type::type_id target_raw_id = g_raw_type_list[other.m_id]; + if (source_raw_id == target_raw_id) + return true; + + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * source_raw_id; + for (int i = 0; i < RTTR_MAX_INHERIT_TYPES_COUNT; ++i) + { + const type::type_id currId = g_base_class_list[row + i]; + if (currId == target_raw_id) + return true; + if (currId == 0) // invalid id + return false; + } + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void* type::apply_offset(void* ptr, const type& source_type, const type& target_type) +{ + type::type_id source_raw_id = g_raw_type_list[source_type.m_id]; + const type::type_id target_raw_id = g_raw_type_list[target_type.m_id]; + + if (source_raw_id == target_raw_id || ptr == nullptr) + return ptr; + + const detail::derived_info info = g_get_derived_info_func_list[source_raw_id](ptr); + source_raw_id = g_raw_type_list[info._type.m_id]; + if (source_raw_id == target_raw_id) + return info._ptr; + + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * source_raw_id; + for (int i = 0; i < RTTR_MAX_INHERIT_TYPES_COUNT; ++i) + { + const type::type_id currId = g_base_class_list[row + i]; + if (currId == target_raw_id) + return g_conversion_list[row + i](info._ptr); + if (currId == 0) // invalid id + return nullptr; + } + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::create_from_ptr(void* ptr) const +{ + return g_variant_create_list[m_id](ptr); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<type> type::get_base_classes() const +{ + const type::type_id raw_id = g_raw_type_list[m_id]; + vector<type> result; + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * raw_id; + for (int i = 0; i < RTTR_MAX_INHERIT_TYPES_COUNT; ++i) + { + const type::type_id currId = g_base_class_list[row + i]; + if (currId != 0) // invalid id + result.push_back(currId); + else + break; + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<type> type::get_derived_classes() const +{ + const type::type_id raw_id = g_raw_type_list[m_id]; + vector<type> result; + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * raw_id; + for (int i = 0; i < RTTR_MAX_INHERIT_TYPES_COUNT; ++i) + { + const type::type_id currId = g_derived_class_list[row + i]; + if (currId != 0) // invalid id + result.push_back(currId); + else + break; + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_class() const +{ + return g_is_class_list[m_id]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_enumeration() const +{ + return g_is_enum_list[m_id]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_array() const +{ + return g_is_array_list[m_id]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_pointer() const +{ + return g_is_pointer_list[m_id]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::is_primitive() const +{ + return g_is_primitive_list[m_id]; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::vector<type> type::get_types() +{ + std::vector<type> result; + result.reserve(g_name_to_id->size()); + for (const auto& value : *g_name_to_id) + { + result.push_back(value.second); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +constructor type::get_constructor(const std::vector<type>& args) const +{ + if (is_class()) + { + const auto classPtr = g_class_data_list[get_raw_type().get_id()].get(); + if (!classPtr) + return constructor(); + + for (const auto& ctor : classPtr->_ctorList) + { + if (detail::reflection_database::does_signature_match_arguments(ctor->get_parameter_types(), args)) + return constructor(ctor.get()); + } + } + else + { + const auto& ctor = g_ctor_list[get_raw_type().get_id()]; + if (detail::reflection_database::does_signature_match_arguments(ctor->get_parameter_types(), args)) + return constructor(ctor.get()); + } + + return constructor(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<constructor> type::get_constructors() const +{ + if (is_class()) + { + const auto classPtr = g_class_data_list[get_raw_type().get_id()].get(); + if (!classPtr) + return { constructor() }; + + vector<constructor> result; + result.reserve(classPtr->_ctorList.size()); + for (const auto& ctor : classPtr->_ctorList) + { + result.push_back(constructor(ctor.get())); + } + return result; + } + else + { + const auto ctor = g_ctor_list[g_raw_type_list[m_id]].get(); + if (ctor) + return { constructor(ctor) }; + } + + return std::vector<constructor>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::create(vector<detail::argument> args) const +{ + if (is_class()) + { + const auto classPtr = g_class_data_list[get_raw_type().get_id()].get(); + if (!classPtr) + return variant(); + + for (const auto& ctor : classPtr->_ctorList) + { + if (detail::reflection_database::does_signature_match_arguments(ctor->get_parameter_types(), + detail::reflection_database::extract_types(args))) + { + return ctor->invoke_variadic(args); + } + } + } + else + { + const auto ctor = g_ctor_list[get_raw_type().get_id()].get(); + if (ctor) + return ctor->invoke_variadic(args); + } + + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +destructor type::get_destructor() const +{ + return destructor(g_dtor_list[m_id].get()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void type::destroy(variant& obj) const +{ + const auto dtor = g_dtor_list[m_id].get(); + if (dtor) + dtor->invoke(obj); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +property type::get_property(const std::string& name) const +{ + if (const auto& classPtr = g_class_data_list[get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_property(name, classPtr->_property_map)) + return property(ret); + } + + // search in the base classes, but not recursively, otherwise we would search twice + for (const auto& type : get_base_classes()) + { + if (const auto& classPtr = g_class_data_list[type.get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_property(name, classPtr->_property_map)) + return property(ret); + } + } + + return property(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::get_property_value(const std::string& name, detail::instance obj) const +{ + const auto prop = get_property(name); + return prop.get_value(obj); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::get_property_value(const std::string& name) +{ + const auto prop = get_global_property(name); + return prop.get_value(empty_instance()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::set_property_value(const std::string& name, detail::instance obj, detail::argument arg) const +{ + const auto prop = get_property(name); + return prop.set_value(obj, arg); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool type::set_property_value(const std::string& name, detail::argument arg) +{ + const auto prop = get_global_property(name); + return prop.set_value(empty_instance(), arg); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +namespace +{ + +void retrieve_sorted_properties(vector<detail::property_container_base*>& result, + const detail::reflection_database::property_map& prop_map) +{ + set<detail::reflection_database::property_container::size_type> sorted_properties; + for (const auto& prop : prop_map) + { + sorted_properties.insert(prop.second); + } + for (const auto& prop_id : sorted_properties) + { + result.push_back((*g_property_list)[prop_id].get()); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void retrieve_sorted_methods(vector<detail::method_container_base*>& result, + const detail::reflection_database::method_map& meth_map) +{ + set<detail::reflection_database::method_container::size_type> sorted_methods; + for (const auto& meth : meth_map) + { + sorted_methods.insert(meth.second); + } + for (const auto& prop_id : sorted_methods) + { + result.push_back((*g_method_list)[prop_id].get()); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace anonymous + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<property> type::get_properties() const +{ + vector<property> result; + vector<detail::property_container_base*> props; + + if (auto classPtr = g_class_data_list[get_raw_type().get_id()].get()) + { + retrieve_sorted_properties(props, classPtr->_property_map); + } + + // search in the base classes, but not recursively + for (const auto& type : get_base_classes()) + { + if (auto classPtr = g_class_data_list[type.get_raw_type().get_id()].get()) + retrieve_sorted_properties(props, classPtr->_property_map); + } + + for (const auto& prop : props) + { + result.push_back(property(prop)); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method type::get_method(const string& name) const +{ + if (const auto classPtr = g_class_data_list[get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_method(name, classPtr->_method_map)) + return method(ret); + } + + // search in the base classes, but not recursively + for (const auto& type : get_base_classes()) + { + if (const auto& classPtr = g_class_data_list[type.get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_method(name, classPtr->_method_map)) + return method(ret); + } + } + + return method(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method type::get_method(const std::string& name, const std::vector<type>& params) const +{ + if (const auto classPtr = g_class_data_list[get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_method(name, params, classPtr->_method_map)) + return method(ret); + } + + // search in the base classes, but not recursively + for (const auto& type : get_base_classes()) + { + if (const auto& classPtr = g_class_data_list[type.get_raw_type().get_id()].get()) + { + if (const auto& ret = detail::reflection_database::find_method(name, params, classPtr->_method_map)) + return method(ret); + } + } + + return method(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +vector<method> type::get_methods() const +{ + vector<method> result; + vector<detail::method_container_base*> methods; + + if (const auto classPtr = g_class_data_list[get_raw_type().get_id()].get()) + { + retrieve_sorted_methods(methods, classPtr->_method_map); + } + + // search in the base classes, but not recursively + for (const auto& type : get_base_classes()) + { + if (const auto classPtr = g_class_data_list[type.get_raw_type().get_id()].get()) + retrieve_sorted_methods(methods, classPtr->_method_map); + } + + for (const auto& meth : methods) + { + result.push_back(method(meth)); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +property type::get_global_property(const std::string& name) +{ + return property(detail::reflection_database::find_property(name, *g_global_properties)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method type::get_global_method(const std::string& name) +{ + return method(detail::reflection_database::find_method(name, *g_global_methods)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +method type::get_global_method(const std::string& name, const std::vector<type>& params) +{ + return method(detail::reflection_database::find_method(name, params, *g_global_methods)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::vector<method> type::get_global_methods() +{ + vector<method> result; + vector<detail::method_container_base*> methods; + + retrieve_sorted_methods(methods, *g_global_methods); + + for (const auto& meth : methods) + { + result.push_back(method(meth)); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::vector<property> type::get_global_properties() +{ + vector<property> result; + vector<detail::property_container_base*> properties; + + retrieve_sorted_properties(properties, *g_global_properties); + + for (const auto& prop : properties) + { + result.push_back(property(prop)); + } + + return result; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +enumeration type::get_enumeration() const +{ + return enumeration(g_enumeration_list[get_raw_type().get_id()].get()); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::invoke(const std::string& name, detail::instance obj, std::vector<detail::argument> args) const +{ + const auto meth = get_method(name, detail::reflection_database::extract_types(args)); + return meth.invoke_variadic(obj, args); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant type::invoke(const std::string& name, std::vector<detail::argument> args) +{ + const auto meth = get_global_method(name, detail::reflection_database::extract_types(args)); + return meth.invoke_variadic(empty_instance(), args); +} + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +type type::get(const char* name) +{ + const auto itr = g_name_to_id->find(name); + if (itr != g_name_to_id->end()) + return type(itr->second); + else + return type(); + +} + +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////// + +type type::register_type(const char* name, + const type& raw_type, + vector<detail::base_class_info> base_classes, + detail::derived_info(*get_derived_func)(void*), + variant(*variant_create_func)(void*), + bool is_class, + bool is_enum, + bool is_array, + bool is_pointer, + bool is_primitive) +{ + std::lock_guard<std::mutex> lock(register_type_mutex); + + using namespace detail; + init_globals(); + reflection_database& db = reflection_database::instance(); + { + const auto itr = g_name_to_id->find(name); + if (itr != g_name_to_id->end()) + return type(itr->second); + } + + const auto ret = g_name_to_id->emplace(name, ++db.type_id_counter); + if (ret.second) + { + g_name_list[db.type_id_counter] = name; + const type::type_id raw_id = ((raw_type.get_id() == 0) ? db.type_id_counter : raw_type.get_id()); + g_raw_type_list[db.type_id_counter] = raw_id; + g_get_derived_info_func_list[raw_id] = get_derived_func; + g_variant_create_list[db.type_id_counter] = variant_create_func; + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * raw_id; + int index = 0; + // remove double entries; can only be happen for virtual inheritance case + set<type> double_entries; + for (auto itr = base_classes.rbegin(); itr != base_classes.rend();) + { + if (double_entries.find(itr->_base_type) == double_entries.end()) + { + double_entries.insert(itr->_base_type); + ++itr; + } + else + { + itr = vector<detail::base_class_info>::reverse_iterator(base_classes.erase((++itr).base())); + } + } + + for (const auto& type : base_classes) + { + g_base_class_list[row + index] = type._base_type.get_id(); + g_conversion_list[row + index] = type._rttr_cast_func; + ++index; + } + + for (const auto& type : base_classes) + { + const int row = RTTR_MAX_INHERIT_TYPES_COUNT * type._base_type.get_raw_type().get_id(); + for (int i = 0; i < RTTR_MAX_INHERIT_TYPES_COUNT; ++i) + { + if (g_derived_class_list[row + i] == 0) + { + g_derived_class_list[row + i] = db.type_id_counter; + break; + } + + } + } + + db.is_class_list [ret.first->second] = is_class; + db.is_enum_list [ret.first->second] = is_enum; + db.is_array_list [ret.first->second] = is_array; + db.is_pointer_list [ret.first->second] = is_pointer; + db.is_primitive_list[ret.first->second] = is_primitive; + } // else cannot happen! + + return type(ret.first->second); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +detail::type_converter_base* type::get_type_converter(const type& target_type) const +{ + const auto& converter_list = g_type_converter_list[m_id]; + + for (const auto& converter : converter_list) + { + if (converter.get()->_target_type == target_type) + return converter.get(); + } + + return nullptr; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void type::register_type_converter(std::unique_ptr<detail::type_converter_base> converter) const +{ + const auto& converter_list = g_type_converter_list[m_id]; + for (const auto& conv : converter_list) + { + if (conv.get()->_target_type == converter->_target_type) + return; + } + g_type_converter_list[m_id].push_back(move(converter)); + g_type_converter_list[m_id].shrink_to_fit(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +namespace impl +{ + +void register_property(type curr_type, unique_ptr<detail::property_container_base> prop) +{ + if (curr_type.is_class()) + { + const type::type_id raw_id = curr_type.get_raw_type().get_id(); + if (!g_class_data_list[raw_id]) + g_class_data_list[raw_id].reset(new detail::reflection_database::class_data); + + detail::reflection_database::register_property(move(prop), g_class_data_list[raw_id]->_property_map); + } + else + { + detail::reflection_database::register_property(move(prop), detail::reflection_database::instance().global_properties); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void register_method(type curr_type, std::unique_ptr<detail::method_container_base> method) +{ + if (curr_type.is_class()) + { + const type::type_id raw_id = curr_type.get_raw_type().get_id(); + if (!g_class_data_list[raw_id]) + g_class_data_list[raw_id].reset(new detail::reflection_database::class_data); + + detail::reflection_database::register_method(move(method), g_class_data_list[raw_id]->_method_map); + } + else + { + detail::reflection_database::register_method(move(method), detail::reflection_database::instance().global_methods); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void register_constructor(type t, std::unique_ptr<detail::constructor_container_base> ctor) +{ + if (t.is_class()) + { + const type::type_id raw_id = t.get_raw_type().get_id(); + if (!g_class_data_list[raw_id]) + g_class_data_list[raw_id].reset(new detail::reflection_database::class_data); + + g_class_data_list[raw_id]->add_constructor(move(ctor)); + } + else if (t.is_primitive() || t.is_pointer()) + { + g_ctor_list[t.get_raw_type().get_id()] = move(ctor); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void register_destructor(type t, std::unique_ptr<detail::destructor_container_base> dtor) +{ + g_dtor_list[t.get_raw_type().get_id()] = move(dtor); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void register_enumeration(type t, std::unique_ptr<detail::enumeration_container_base> enum_item) +{ + g_enumeration_list[t.get_raw_type().get_id()] = move(enum_item); +} + +} // end namespace impl +} // end namespace rttr diff --git a/src/rttr/type.h b/src/rttr/type.h new file mode 100644 index 00000000..001cd789 --- /dev/null +++ b/src/rttr/type.h @@ -0,0 +1,730 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TYPE_H__ +#define __RTTR_TYPE_H__ + +#include "rttr/base/core_prerequisites.h" + +#include <type_traits> +#include <vector> +#include <string> +#include <memory> + +namespace rttr +{ +class variant; +class constructor; +class destructor; +class method; +class property; +class enumeration; +class type; + +template<typename TargetType, typename SourceType> +TargetType rttr_cast(SourceType object); + +namespace detail +{ +class instance; +class argument; +struct derived_info; +struct base_class_info; +struct type_converter_base; +} // end namespace detail + +namespace impl +{ +template<typename T> +struct MetaTypeInfo; +static type get_invalid_type(); +} // end namespace impl + +/*! + * This class holds the type information for any arbitrary object. + * + * Every class or primitive data type can have an unique type object. + * With the help of this object you can compare unknown types for equality at runtime or introspect the type + * for its properties, methods, enumerations, constructors and destructor. + * + * Preparation + * ----------- + * Before you can retrieve data from type, you have to register your struct or class. + * Therefore use the macro #RTTR_DECLARE_TYPE(Type) to make the type known to the type system. + * + * This example shows a typical usage: +\code{.cpp} + // MyStruct.h + struct MyStruct + { + int i; + }; + + RTTR_DECLARE_TYPE(MyStruct) + +\endcode + * + * Retrieve %type + * ------------------ + * A type object cannot be created. It is only possible to retrieve a type object via three static template member functions: + * + * type::get(const char*) + * + * This function just expects the name of the type. This is useful when you know only the name of the type and cannot include the type itself into the source code. + * The name of the type is the same like you have registered with \ref RTTR_DECLARE_TYPE but as string literal. When you have used a typedef then you need to provide this typedef also as string literal. + * +\code{.cpp} + type::get("int") == type::get("int"); // yields to true + type::get("bool") == type::get("int"); // yields to false + type::get("MyNameSpace::MyStruct") == type::get("MyNameSpace::MyStruct"); // yields to true +\endcode + * + * type::get<T>() + * + * This function just expects one template argument. Use it to check against a known type. + * +\code{.cpp} + type::get<int>() == type::get<int>(); // yields to true + type::get<int>() == type::get<bool>(); // yields to false +\endcode + * + * type::get<T>(T&& obj) + * + * This function is a universal reference and returns from every given object the corresponding type object. + * +\code{.cpp} + int int_obj; + int* int_obj_ptr = &int_obj; + const int* c_int_obj_ptr = int_obj_ptr; + type::get<int>() == type::get(int_obj); // yields to true + type::get<int*>() == type::get(int_obj_ptr); // yields to true + type::get<const int*>() == type::get(c_int_obj_ptr);// yields to true +\endcode + * + * When this function is called for a glvalue expression whose type is a polymorphic class type, + * then the result refers to a \ref type object representing the type of the most derived object. + * +\code{.cpp} + struct Base {}; + struct Derived : Base {}; + Derived d; + Base& base = d; + type::get<Derived>() == type::get(base) // yields to true + type::get<Base>() == type::get(base) // yields to false + + // remark, when called with pointers: + Base* base_ptr = &d; + type::get<Derived>() == type::get(base_ptr); // yields to false + type::get<Base*>() == type::get(base_ptr); // yields to true +\endcode + * + * \remark If the type of the expression is a cv-qualified type, the result of the rttr::type::get expression refers to a rttr::type object representing the cv-unqualified type. + * +\code{.cpp} + class D { ... }; + D d1; + const D d2; + type::get(d1) == type::get(d2); // yields true + type::get<D>() == type::get<const D>(); // yields true + type::get<D>() == type::get(d2); // yields true + type::get<D>() == type::get<const D&>(); // yields true + type::get<D>() == type::get<const D*>(); // yields false +\endcode + * Any `top level` cv-qualifier of the given type `T` will be removed. + * + * + * Copying and Assignment + * ---------------------- + * A \ref type object is lightweight and can be copied by value. However, each copy will refer to the same underlying type. + * + */ +class RTTR_API type +{ + public: + typedef uint16 type_id; + + /*! + * \brief Assigns a type to another one. + * + */ + type(const type& other); + + /*! + * \brief Assigns a type to another one. + * + * \return A type object. + */ + type& operator=(const type& other); + + /*! + * \brief Comparison operator for sorting the type data according to some internal criterion. + * + * \return True if this type is less than the \a other. + */ + bool operator<(const type& other) const; + + /*! + * \brief Comparison operator for sorting the type data according to some internal criterion. + * + * \return True if this type is greater than the \a other. + */ + bool operator>(const type& other) const; + + /*! + * \brief Comparison operator for sorting the type data according to some internal criterion. + * + * \return True if this type is greater than or equal to \a other. + */ + bool operator>=(const type& other) const; + + /*! + * \brief Comparison operator for sorting the type data according to some internal criterion. + * + * \return True if this type is less than or equal to \a other. + */ + bool operator<=(const type& other) const; + + /*! + * \brief Compares this type with the \a other type and returns true + * if both describe the same type, otherwise returns false. + * + * \return True if both type are equal, otherwise false. + */ + bool operator==(const type& other) const; + + /*! + * \brief Compares this type with the \a other type and returns true + * if both describe different types, otherwise returns false. + * + * \return True if both type are \b not equal, otherwise false. + */ + bool operator!=(const type& other) const; + + /*! + * \brief Returns the id of this type. + * + * \note This id is unique at process runtime, + * but the id can be changed every time the process is executed. + * + * \return The type id. + */ + type_id get_id() const; + + /*! + * \brief Returns the unique and human-readable name of the type. + * + * \return The type name. + */ + std::string get_name() const; + + /*! + * \brief Returns true if this type is valid, that means the type holds valid data to a type. + * + * \return True if this type is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Convenience function to check if this \ref type is valid or not. + * + * \return True if this \ref type is valid, otherwise false. + */ + operator bool() const; + + /*! + * \brief Returns a type object which represent the raw type. + * A raw type, is a type type without any qualifiers (const and volatile) nor any pointer. + * + * \remark When the current type is already the raw type, it will return an copy from itself. + * + * \return The corresponding raw type object. + */ + type get_raw_type() const; + + /*! + * \brief Returns a type object for the given template type \a T. + * + * \return type for the template type \a T. + */ + template<typename T> + static type get(); + + /*! + * \brief Returns a type object for the given instance \a object. + * + * \remark If the type of the expression is a cv-qualified type, the result of the type::get() expression refers to a + * type object representing the cv-unqualified type. + * When type::get() is applied to a glvalue expression whose type is a polymorphic class type, + * the result refers to a type object representing the type of the most derived object. + * + * \return type for an \a object of type \a T. + */ + template<typename T> + static type get(T&& object); + + /*! + * \brief Returns the type object for the given \a name. + * + * \remark The search for the type is case sensitive. + * The name itself correspond to the name registered with RTTR_DECLARE_TYPE. + * + * \return type for an \a object of name \a name. + */ + static type get(const char* name); + + /*! + * \brief Returns a list of all registered type objects. + * + * \remark The order of the type object is unspecified. + * + * \return A vector of type objects. + */ + static std::vector<type> get_types(); + + /*! + * \brief Returns true whether the given type is class; that is not an atomic type or a method. + * + * \return True if the type is a class, otherwise false. + */ + bool is_class() const; + + /*! + * \brief Returns true whether the given type represents an enumeration. + * + * \return True if the type is an enumeration, otherwise false. + */ + bool is_enumeration() const; + + /*! + * \brief Returns the enumerator if this type is an enum type; + * otherwise the returned value is \ref enumeration::is_valid "not valid". + * + * \see is_enumeration() + * + * \return A enumeration object. + */ + enumeration get_enumeration() const; + + /*! + * \brief Returns true whether the given type represents an array. + * + * \return True if the type is an array, otherwise false. + */ + bool is_array() const; + + /*! + * \brief Returns true whether the given type represents a pointer. + * + * \return True if the type is a pointer, otherwise false. + */ + bool is_pointer() const; + + /*! + * \brief Returns true whether the given type represents an primitive type (e.g. int, bool, etc.). + * + * \return True if the type is a primitive type, otherwise false. + */ + bool is_primitive() const; + + /*! + * \brief Returns true if this type is derived from the given type \p other, otherwise false. + * + * \return Returns true if this type is a derived type from \p other, otherwise false. + */ + bool is_derived_from(const type& other) const; + + /*! + * \brief Returns true if this type is derived from the given type \a T, otherwise false. + * + * \return Returns true if this type is a derived type from \a T, otherwise false. + */ + template<typename T> + bool is_derived_from() const; + + /*! + * \brief Returns a list of all base classes of this type. + * + * \return A list of type objects. + */ + std::vector<type> get_base_classes() const; + + /*! + * \brief Returns a list of all derived classes of this type. + * + * \return A list of type objects. + */ + std::vector<type> get_derived_classes() const; + + ///////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////////////////// + + /*! + * \brief Returns a public constructor whose parameters match the types in the specified list. + * + * \remark When no parameter list is given, it will be searched for the default constructor. + * + * \return A valid constructor will be returned when the parameter matches the registered constructor; + * otherwise an invalid constructor. + */ + constructor get_constructor(const std::vector<type>& params = std::vector<type>() ) const; + + /*! + * \brief Returns a list of all registered constructors for this type; the order is unspecified. + * + * \return Returns a list of all registered constructors. + */ + std::vector<constructor> get_constructors() const; + + /*! + * \brief Creates an instance of the given type, with the given arguments for the constructor. + * + * \remark When the arguments does not match the parameter list of the constructor then he will not be invoked. + * + * \return Returns an instance of the given type. + */ + variant create(std::vector<detail::argument> args) const; + + /*! + * \brief Returns the corresponding destructor for this type. + * + * \remark When there is no constructor registered for this type, then also the destructor is not available. + * A destructor will always been automatically registered. + * + * \return Returns the destructor for this type. + */ + destructor get_destructor() const; + + /*! + * \brief Destroys the given object \p obj. + * + * \remark When the \p obj could be destroyed the given \p obj is invalid after calling this method; + * Otherwise it is still valid. + */ + void destroy(variant& obj) const; + + + /*! + * \brief Returns a property with the name \p name. + * + * \remark When there exists not property with the name \p name, and invalid property is returned. + * + * \return A property with name \p name. + */ + property get_property(const std::string& name) const; + + /*! + * \brief Returns a list of all registered properties for this type and + * all its base classes. + * + * \return A vector with properties. + */ + std::vector<property> get_properties() const; + + /*! + * \brief Returns a global property with the name \p name. + * + * \remark When there exists not property with the name \p name, and invalid property is returned. + * + * \return A property with name \p name. + */ + static property get_global_property(const std::string& name); + + /*! + * \brief Returns a list of all registered global properties. + * + * \return A vector with properties. + */ + static std::vector<property> get_global_properties(); + + + /*! + * \brief Returns the property value of property named \p name from the instance \p obj. + * + * \remark When the given instance is empty, the value of global property will be tryed to returned. + * + * \return A variant containing the value of the property. + */ + variant get_property_value(const std::string& name, detail::instance obj) const; + + /*! + * \brief Returns the property value of property named \p name. + * + * \return A variant containing the value of the property. + */ + static variant get_property_value(const std::string& name); + + /*! + * \brief This function will set the given value \p arg to a property named \p name to the instance \p obj. + * + * \remark When the given instance is empty, the value of a global property with name \p name will be tryed to set. + * + * \return A bool value, which is true, when the value could be set, otherwise false. + */ + bool set_property_value(const std::string& name, detail::instance obj, detail::argument arg) const; + + /*! + * \brief This function will set the given value \p arg to a property named \p name. + * + * \return A bool value, which is true, when the value could be set, otherwise false. + */ + static bool set_property_value(const std::string& name, detail::argument arg); + + + /*! + * \brief Returns a method with the name \p name. + * + * \remark When there exists not method with the name \p name, then an invalid method is returned. + * + * \return A method with name \p name. + */ + method get_method(const std::string& name) const; + + /*! + * \brief Returns a method with the name \p name which match the given parameter list \p params. + * + * \remark When there exists not method with the name \p name and matching parameter list \p params, + * then an invalid method is returned. + * + * \return A method with name \p name. + */ + method get_method(const std::string& name, const std::vector<type>& params) const; + + /*! + * \brief Returns a list of all registered methods for this type and + * all its base classes. + * + * \return A vector with method objects + */ + std::vector<method> get_methods() const; + + /*! + * \brief Returns a global method with the name \p name. + * + * \remark When there exists not method with the name \p name, and invalid method is returned. + * + * \return A method with name \p name. + */ + static method get_global_method(const std::string& name); + + /*! + * \brief Returns a global method with the name \p name which match the given parameter list \p params. + * + * \remark When there exists not method with the name \p name and matching parameter list \p params, + * then an invalid method is returned. + * + * \return A method with name \p name and parameter signature \p params. + */ + static method get_global_method(const std::string& name, const std::vector<type>& params); + + /*! + * \brief Returns a list of all registered global methods. + * + * \return A vector with methods. + */ + static std::vector<method> get_global_methods(); + + + /*! + * \brief Invokes the method represented by the current instance \p object. + * + * \remark When it's a static method you still need to provide an instance object, use therefore the function `empty_instance()`. + * + * \return The a variant object containing the possible return value, + * otherwise when it is a void function an empty but valid variant object. + */ + variant invoke(const std::string& name, detail::instance obj, std::vector<detail::argument> args) const; + + /*! + * \brief Invokes a global method named \p name with the specified argument \p args. + * + * \return The a variant object containing the possible return value, + * otherwise when it is a void function an empty but valid variant object. + */ + static variant invoke(const std::string& name, std::vector<detail::argument> args); + + /*! + * \brief Register a converter func `F`. + * This function converts a source Type to a target type. + * The signature of this function has to be the following: <TargetType (SourceType, bool& ok)> + * e.g.: + \code{.cpp} + std::string conv_func(int value, bool& ok) + { + std::string result = std::to_string(value); + ok = true; + return result; + } + //... + type::register_converter_func(conv_func); + \endcode + */ + template<typename F> + static void register_converter_func(F func); + + private: + + /*! + * Constructs an empty and invalid type object. + */ + type(); + + /*! + * \brief Constructs a valid type object. + * + * \param id The unique id of the data type. + */ + type(type_id id); + + /*! + * \brief This function try to convert the given pointer \p ptr from the type \p source_type + * to the target type \p target_type. + * + * \remark The returned pointer is always the raw type of \p target_type. You do not have to use this function by your own. + * + * \return Returns the converted pointer; when the conversion fails is a null pointer is returned. + */ + static void* apply_offset(void* ptr, const type& source_type, const type& target_type); + + /*! + * \brief When for the current type instance a converter function to type \p target_type was registered, + * then this function returns a valid pointer to a type_converter_base object. + * Otherwise this function returns a nullptr. + * + */ + detail::type_converter_base* get_type_converter(const type& target_type) const; + + ///////////////////////////////////////////////////////////////////////////////// + // now comes the register functions + + /*! + * \brief Register the type info for the given name + * + * \remark When a type with the given name is already registered, + * then the type for the already registered type will be returned. + * + * \return A valid type object. + */ + static type register_type(const char* name, + const type& raw_type, + std::vector<detail::base_class_info> base_classes, + detail::derived_info(*get_derived_func)(void*), + variant(*variant_create_func)(void*), + bool is_class, + bool is_enum, + bool is_array, + bool is_pointer, + bool is_primitive); + + void register_type_converter(std::unique_ptr<detail::type_converter_base> converter) const; + + variant create_from_ptr(void* ptr) const; + + template<typename T> + friend struct impl::MetaTypeInfo; + + template<typename T> + friend class class_; + + friend type impl::get_invalid_type(); + friend class variant; + + friend class detail::instance; + + template<typename TargetType, typename SourceType> + friend TargetType rttr_cast(SourceType object); + + private: + type_id m_id; +}; + +#ifdef DOXYGEN +/*! + * \brief This macro makes the type \p Type known to the \ref rttr::type "type" system. + * + * The macro should be placed directly under declaration of the custom class or struct of \p Type. + * So the \ref rttr::type "type" class can access it in every translation unit (*.cpp file). + * + * When using a \p Type without this registration, it will lead to a compile time error + * with following message: *The given type T is not registered to the type system; please register with RTTR_DECLARE_TYPE.* + * + * The following example will demonstrate the usage: + \code{.cpp} + // MyStruct.h + struct MyStruct + { + bool visible; + }; + + RTTR_DECLARE_TYPE(MyStruct) + \endcode + * + * When MyStruct is in a namespace, make sure you putt the macro outside the namespace, + * otherwise \ref rttr::type "type" cannot access the \p Type. + \code{.cpp} + namespace NSMyStruct + { + } + + RTTR_DECLARE_TYPE(NSMyStruct::MyStruct) + \endcode + */ +#define RTTR_DECLARE_TYPE(Type) + +/*! + * \brief This macro will define three common variants of the given type \p Type. + * That are: `Type`, `Type*`, `const Type*` + * So it is basically a shortcut instead of writing three times \ref RTTR_DECLARE_TYPE. + */ +#define RTTR_DECLARE_STANDARD_TYPE_VARIANTS(Type) + + +/*! + * \brief This macro makes the type \p Type immediately known to the \ref rttr::type "type" system. + * + * Place this macro inside the global namespace of one translation unit. Normally it is placed + * inside the corresponding cpp file of type \p Type. + * + * The reason for this macro is, to make sure that the given \p Type is registered before main was executed. + * Another way to execute the registration process is to call rttr::type::get<Type>(), this will invoke + * the registration function registered with \ref RTTR_DECLARE_TYPE. + * + \code{.cpp} + // MyStruct.cpp + RTTR_DEFINE_TYPE(MyStruct) + \endcode + * + */ +#define RTTR_DEFINE_TYPE(Type) + +#endif + +} // end namespace rttr + +#include "rttr/impl/type_impl.h" + +#endif // __RTTR_TYPE_H__ diff --git a/src/rttr/variant.cpp b/src/rttr/variant.cpp new file mode 100644 index 00000000..a794eed5 --- /dev/null +++ b/src/rttr/variant.cpp @@ -0,0 +1,1038 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/variant.h" +#include "rttr/detail/std_conversion_functions.h" +#include "rttr/variant_array.h" +#include <limits> + +#include <string> + +// needed for type::get<void>() +#include "rttr/rttr_enable.h" +#include "rttr/detail/array_container.h" + +RTTR_DECLARE_TYPE(void) +RTTR_DECLARE_TYPE(int) +RTTR_DECLARE_TYPE(bool) +RTTR_DECLARE_TYPE(float) +RTTR_DECLARE_TYPE(double) +RTTR_DECLARE_TYPE(std::string) + +namespace rttr +{ +namespace detail +{ + variant create_void_variant() + { + variant var; + var._holder = new variant::variant_container<void>(); + return var; + } + + variant void_variant = create_void_variant(); +} // end namespace impl + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::to_int(bool *ok) const +{ + return convert<int>(ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::to_bool() const +{ + return convert<bool>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::to_string(bool *ok) const +{ + return convert<std::string>(ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::to_float(bool* ok) +{ + return convert<float>(ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::to_double(bool* ok) +{ + return convert<double>(ok); +} + +///////////////////////////////////////////////////////////////////////////////// + +variant_array variant::to_array() const +{ + if (_holder) + return variant_array(_holder->to_array()); + else + return variant_array(); +} + +///////////////////////////////////////////////////////////////////////////////// + +bool variant::can_convert(const type& target_type) const +{ + if (_holder) + return _holder->can_convert(target_type); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +bool variant::can_convert<variant_array>() const +{ + if (_holder) + return _holder->is_array(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////// + +bool variant::convert(const type& target_type) +{ + if (!_holder) + return false; + + bool ok = false; + variant new_var; + const type& source_type = _holder->get_type(); + if (target_type == source_type) + { + return true; + } + else if (target_type == type::get<std::string>()) + { + new_var = to_string(&ok); + } + else if (target_type == type::get<int>()) + { + new_var = to_int(&ok); + } + else if (target_type == type::get<bool>()) + { + new_var = to_bool(); + ok = true; + } + else if (target_type == type::get<float>()) + { + new_var = to_float(&ok); + } + else if (target_type == type::get<double>()) + { + new_var = to_double(&ok); + } + else + { + if (const auto& converter = source_type.get_type_converter(target_type)) + { + void* data = _holder->get_ptr(); + new_var = converter->to_variant(data, ok); + } + else + { + void* data = _holder->get_raw_ptr(); + void* d_ptr = type::apply_offset(data, source_type, target_type); + if (d_ptr) + { + new_var = target_type.create_from_ptr(d_ptr); + if (new_var.is_valid()) + ok = true; + } + } + + } + + if (ok) + swap(new_var); + + return ok; +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +std::string variant::convert<std::string>(bool* ok) const +{ + if (_holder) + return _holder->to_string(ok); + else + { + if (ok) + *ok = false; + return std::string(); + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +int variant::convert<int>(bool* ok) const +{ + if (_holder) + return _holder->to_int(ok); + else + { + if (ok) + *ok = false; + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +bool variant::convert<bool>(bool* ok) const +{ + if (_holder) + return _holder->to_bool(ok); + else + { + if (ok) + *ok = false; + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +float variant::convert<float>(bool* ok) const +{ + if (_holder) + return _holder->to_float(ok); + else + { + if (ok) + *ok = false; + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +double variant::convert<double>(bool* ok) const +{ + if (_holder) + return _holder->to_double(ok); + else + { + if (ok) + *ok = false; + return 0; + } +} + +///////////////////////////////////////////////////////////////////////////////// + +template<> +variant_array variant::convert<variant_array>(bool* ok) const +{ + variant_array result; + + if (_holder) + result = _holder->to_array(); + + if (ok && result.is_valid()) + *ok = false; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +variant::variant_container<void>::variant_container() +{ + +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// void + +variant::variant_container_base* variant::variant_container<void>::clone() const +{ + return (new variant_container<void>()); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<void>::get_type() const +{ + return type::get<void>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<void>::get_ptr() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<void>::get_raw_type() const +{ + return type::get<void>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<void>::get_raw_ptr() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<void>::is_array() const +{ + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<void>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<void>::can_convert(const type& target_type) const +{ + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<void>::to_string(bool* ok) const +{ + if (ok) + *ok = false; + + return std::string(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<void>::to_int(bool* ok) const +{ + if (ok) + *ok = false; + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<void>::to_bool(bool* ok) const +{ + if (ok) + *ok = false; + + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<void>::to_float(bool* ok) const +{ + if (ok) + *ok = false; + + return 0.0f; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<void>::to_double(bool* ok) const +{ + if (ok) + *ok = false; + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// std::string + +variant::variant_container<std::string>::variant_container(const std::string& arg) +: _value(arg) +{ + +} + +variant::variant_container<std::string>::variant_container(std::string&& arg) +: _value(std::move(arg)) +{ +} + +variant::variant_container_base* variant::variant_container<std::string>::clone() const +{ + return (new variant_container<std::string>(_value)); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<std::string>::get_type() const +{ + return type::get<std::string>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<std::string>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<std::string>::get_raw_type() const +{ + return type::get<detail::raw_type<std::string>::type>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<std::string>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<std::string>::is_array() const +{ + return detail::is_array<detail::raw_type<std::string>::type>::value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<std::string>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<std::string>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<std::string>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<int>()) + return true; + else if (target_type == type::get<bool>()) + return true; + else if (target_type == type::get<float>()) + return true; + else if (target_type == type::get<double>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<std::string>::to_string(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<std::string>::to_int(bool* ok) const +{ + return detail::string_to_int(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<std::string>::to_bool(bool* ok) const +{ + return detail::string_to_bool(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<std::string>::to_float(bool* ok) const +{ + return detail::string_to_float(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<std::string>::to_double(bool* ok) const +{ + return detail::string_to_double(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// int + +variant::variant_container<int>::variant_container(const int& arg) +: _value(arg) +{ + +} + +variant::variant_container<int>::variant_container(int&& arg) +: _value(std::move(arg)) +{ +} + +variant::variant_container_base* variant::variant_container<int>::clone() const +{ + return (new variant_container<int>(_value)); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<int>::get_type() const +{ + return type::get<int>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<int>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<int>::get_raw_type() const +{ + return type::get<detail::raw_type<int>::type>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<int>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<int>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<int>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<std::string>()) + return true; + else if (target_type == type::get<bool>()) + return true; + else if (target_type == type::get<float>()) + return true; + else if (target_type == type::get<double>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<int>::is_array() const +{ + return detail::is_array<detail::raw_type<int>::type>::value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<int>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<int>::to_string(bool* ok) const +{ + return detail::int_to_string(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<int>::to_int(bool* ok) const +{ + if (ok) + *ok = true; + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<int>::to_bool(bool* ok) const +{ + if (ok) + *ok = true; + + return (_value != 0) ? true : false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<int>::to_float(bool* ok) const +{ + if (ok) + *ok = true; + return static_cast<float>(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<int>::to_double(bool* ok) const +{ + if (ok) + *ok = true; + return static_cast<double>(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// bool + +variant::variant_container<bool>::variant_container(const bool& arg) +: _value(arg) +{ + +} + +variant::variant_container<bool>::variant_container(bool&& arg) +: _value(std::move(arg)) +{ +} + +variant::variant_container_base* variant::variant_container<bool>::clone() const +{ + return (new variant_container<bool>(_value)); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<bool>::get_type() const +{ + return type::get<bool>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<bool>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<bool>::get_raw_type() const +{ + return type::get<detail::raw_type<bool>::type>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<bool>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<bool>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<bool>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<std::string>()) + return true; + else if (target_type == type::get<int>()) + return true; + else if (target_type == type::get<float>()) + return true; + else if (target_type == type::get<double>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<bool>::is_array() const +{ + return detail::is_array<detail::raw_type<bool>::type>::value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<bool>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<bool>::to_string(bool* ok) const +{ + return (_value ? "true" : "false"); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<bool>::to_int(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<bool>::to_bool(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<bool>::to_float(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<bool>::to_double(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// float + +variant::variant_container<float>::variant_container(const float& arg) +: _value(arg) +{ + +} + +variant::variant_container<float>::variant_container(float&& arg) +: _value(std::move(arg)) +{ +} + +variant::variant_container_base* variant::variant_container<float>::clone() const +{ + return (new variant_container<float>(_value)); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<float>::get_type() const +{ + return type::get<float>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<float>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<float>::get_raw_type() const +{ + return type::get<detail::raw_type<float>::type>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<float>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<float>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<float>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<std::string>()) + return true; + else if (target_type == type::get<int>()) + return true; + else if (target_type == type::get<bool>()) + return true; + else if (target_type == type::get<double>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<float>::is_array() const +{ + return detail::is_array<detail::raw_type<float>::type>::value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<float>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<float>::to_string(bool* ok) const +{ + return detail::float_to_string(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<float>::to_int(bool* ok) const +{ + if (ok) + *ok = true; + + return static_cast<int>(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<float>::to_bool(bool* ok) const +{ + if (ok) + *ok = true; + + return (std::abs(_value) <= std::numeric_limits<float>::epsilon()) ? false : true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<float>::to_float(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<float>::to_double(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// double + +variant::variant_container<double>::variant_container(const double& arg) +: _value(arg) +{ + +} + +variant::variant_container<double>::variant_container(double&& arg) +: _value(std::move(arg)) +{ +} + +variant::variant_container_base* variant::variant_container<double>::clone() const +{ + return (new variant_container<double>(_value)); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<double>::get_type() const +{ + return type::get<double>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<double>::get_ptr() const +{ + return const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_value))); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +type variant::variant_container<double>::get_raw_type() const +{ + return type::get<detail::raw_type<double>::type>(); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +void* variant::variant_container<double>::get_raw_ptr() const +{ + return detail::get_void_ptr(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<double>::is_array() const +{ + return detail::is_array<detail::raw_type<double>::type>::value; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +detail::array_container_base* variant::variant_container<double>::to_array() const +{ + return nullptr; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<double>::can_convert(const type& target_type) const +{ + const auto& source_type = type::get<double>(); + if (source_type == target_type) + return true; + else if (target_type == type::get<std::string>()) + return true; + else if (target_type == type::get<int>()) + return true; + else if (target_type == type::get<bool>()) + return true; + else if (target_type == type::get<float>()) + return true; + else if (source_type.get_type_converter(target_type)) + return true; + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +std::string variant::variant_container<double>::to_string(bool* ok) const +{ + return detail::double_to_string(_value, ok); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +int variant::variant_container<double>::to_int(bool* ok) const +{ + if (ok) + *ok = true; + + return static_cast<int>(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +bool variant::variant_container<double>::to_bool(bool* ok) const +{ + if (ok) + *ok = true; + + return (std::abs(_value) <= std::numeric_limits<double>::epsilon()) ? false : true; +} + +//////////////////////////////////////////////////////////////////////////////////////// + +float variant::variant_container<double>::to_float(bool* ok) const +{ + if (ok) + *ok = true; + + return static_cast<float>(_value); +} + +//////////////////////////////////////////////////////////////////////////////////////// + +double variant::variant_container<double>::to_double(bool* ok) const +{ + if (ok) + *ok = true; + + return _value; +} + +} // end namespace rttr diff --git a/src/rttr/variant.h b/src/rttr/variant.h new file mode 100644 index 00000000..debcbbb1 --- /dev/null +++ b/src/rttr/variant.h @@ -0,0 +1,567 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_VARIANT_H__ +#define __RTTR_VARIANT_H__ + +#include "rttr/base/core_prerequisites.h" +#include "rttr/type.h" + +#include <type_traits> +#include <cstddef> +#include <algorithm> + +namespace rttr +{ + +class variant_array; + +namespace detail +{ + RTTR_LOCAL variant create_void_variant(); + class argument; + class instance; + class array_container_base; +} + +/*! + * The variant class allows to store data of any type and convert between these types transparently. + * + * This class serves as container for any given single type. It can hold one value at a time + * (using containers you can hold multiple types e.g. `std::vector<int>`). Remark that the content is copied + * into the variant class. Even raw arrays (e.g. `int[10]`) are copied. + * + * In order to use this class with your custom `Type`, you have to register it with \ref #RTTR_DECLARE_TYPE(Type). + * This class is mainly used for returning values from calls. See et.al. \ref property::get_value() or \ref method::invoke(). + * + * + * Copying and Assignment + * ---------------------- + * A \ref variant object is can be copied and assigned, however each copy will perform a copy of the contained value. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + variant var; + var = 23; // copy integer + int x = var.to_int(); // x = 23 + + var = std::string("Hello World"); // variant contains now a std::string + var = "Hello World"; // variant contains now a char[12] array + int y = var.to_int(); // y = 0, because invalid conversion + std::string text = var.to_string(); // text = "Hello World", char array to string converted + + var = "42"; // contains now char[3] array + std::cout << var.to_int(); // convert char array to integer and prints "42" + + int my_array[100]; + var = my_array; // copies the content of my_array into var + auto& arr = var.get_value<int[100]>(); // extracts the content of var by reference +\endcode + * + * + * It's of course also possible to store own custom types in a variant, + * take a look at following code: + * +\code{.cpp} + struct custom_type + { + //... + }; + + variant var = custom_type(...); + bool b = var.is_type<custom_type>(); // b = true + custom_type& value = var.get_value<custom_type>(); // extracts the value by reference +\endcode + * + * The variant class allows to convert also custom types, + * therefore you have to register a convert function: + * +\code{.cpp} + std::string converter_func(const custom_type& value, bool& ok) + { + ok = true; + // convert value to std::string + return std::string(...); + } + + //... + variant var = custom_type(...); + var.can_convert<std::string>(); // return false + // will register a convert from custom_type to std::string + type::register_converter_func(converter_func); + + var.can_convert<std::string>(); // return true + var.to_string(); // converts from custom_type to std::string +\endcode + * + * \see variant_array + */ +class RTTR_API variant +{ + public: + /*! + * \brief Constructs an invalid variant. That means a valid which contains no data. + * + * \see is_valid() + */ + variant(); + + /*! + * \brief Constructs a new variant with the given value \p param of type \p T. + * The value will be copied into the variant. + */ + template<typename T> + variant(const T& param); + + /*! + * \brief Constructs a new variant with the new value \p param. + * The value will be moved into the variant. + */ + template<typename T> + variant(T&& param +#ifndef DOXYGEN + , typename std::enable_if<!std::is_same<variant&, T>::value >::type* = 0 + , typename std::enable_if<!std::is_const<T>::value >::type* = 0 +#endif + ); + + /*! + * \brief Constructs a new variant object from the given variant \p other. + */ + variant(const variant& other); + + /*! + * \brief Constructs a new variant via move constructor. + */ + variant(variant&& other); + + /*! + * \brief Destroys the variant and the contained object. + */ + ~variant(); + + /*! + * \brief Swaps the content of this variant with the \p other variant. + */ + void swap(variant& other); + + /*! + * Assigns the value of the \p other object to this variant. + * + * \return A reference to the variant with the new data. + */ + template<typename T> + variant& operator=(T&& other); + + /*! + * Assigns the value of the \a other variant to this variant. + * + * \return A reference to the variant with the new data. + */ + variant& operator=(variant&& other); + + /*! + * Assigns the value of the \a other variant to this variant. + * + * \return A reference to the variant with the new data. + */ + variant& operator=(const variant& other); + + /*! + * Returns true if this variant data is of the given template type \a T. + * + * \return True if variant is the same like \a T, otherwise false. + */ + template<typename T> + bool is_type() const; + + /*! + * \brief Returns the type object of underlying data type. + * + * \remark When the variant has not stored any data, then an invalid type object is returned. + * + * \return type of the underlying data type. + */ + type get_type() const; + + /*! + * Returns true if this variant is valid, that means the variant is holding some data. + * + * When the variant doesn't hold any data it will return false. + * + * \return True if this variant is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Returns true if the contained value can be converted to the given type \p T. + * Otherwise false. + * + * \return True if this variant can be converted to \p T; otherwise false. + */ + template<typename T> + bool can_convert() const; + + /*! + * \brief Returns true if the contained value can be converted to the given type \p target_type; + * otherwise false. + * + * \return True if this variant can be converted to \p target_type; otherwise false. + */ + bool can_convert(const type& target_type) const; + + /*! + * \brief Converts the containing variant internally to the given type \p target_type. + * When the conversion was successfully the function will return true. + * When the conversion fails, then the containing variant value stays the same and the function will return false. + * + * A variant containing a pointer to a custom type will also convert and return true + * for this function if a \ref rttr_cast to the type described by \p target_type would succeed. + * + * \see can_convert() + * + * \return True if this variant can be converted to \p target_type; otherwise false. + */ + bool convert(const type& target_type); + + /*! + * \brief Returns a reference to the containing value as type \p T. + * + * \remark Only call this method when it is possible to return the containing value as the given type \p T. + * Use therefore the method is_type(). + * Otherwise the call leads to undefined behaviour. + * Make sure you don't clean this variant, when you still hold a reference to the containing value. + * + * \see is_type() + * + * \return A reference to the stored value. + */ + template<typename T> + T& get_value() const; + + + /*! + * \brief Converts the containing value to type \p T and returns the value. + * If \a ok is non-null: \a *ok is set to true if the value could be converted to an \p T; otherwise \a *ok is set to false. + * + * + * \remark Only call this method when it is possible to return the containing value as type \p T. + * Use therefore the method can_convert(). + * Otherwise the call leads to undefined behaviour. + * + * \see can_convert() + * + * \return The converted value as type \p T. + */ + template<typename T> + T convert(bool* ok = nullptr) const; + + /*! + * \brief Returns the containing variant as \p int value when the type is an integer, + * or when a conversion function for the underlying type to \p int was registered; + * otherwise returns 0. + * + * If \a ok is non-null: \a *ok is set to true if the value could be converted to an \p int; otherwise \a *ok is set to false. + * + * \see can_convert(), is_type() + * + * \return An integer value. + */ + int to_int(bool *ok = nullptr) const; + + /*! + * \brief Returns the containing variant as \p std::string when the type is an \p std::string, + * or when a conversion function for the underlying type to \p std::string was registered; + * otherwise returns an empty default constructed \p std::string object is returned. + * + * If \a ok is non-null: \a *ok is set to true if the value could be converted to an \p std::string; otherwise \a *ok is set to false. + * + * \see can_convert(), is_type() + * + * \return An std::string value. + */ + std::string to_string(bool *ok = nullptr) const; + + /*! + * \brief Returns the variant as a \p bool if the variant has is_type() bool. + * + * Returns true if the variant is of type \p int, \p bool, \p float, \p double and the value is non-zero. + * or if the variant has type `std::string` or `char[]` and its lower-case content is not one of the following: + * empty, \p "0" or \p "false"; otherwise returns false. + * + * When the type is a custom type and a conversion function to \p bool was registered, + * then this call will try to convert the value to \p bool. + * + * \see can_convert(), is_type() + * + * \return A bool value. + */ + bool to_bool() const; + + /*! + * \brief Returns the containing variant as \p float value when the type is a \p float, + * or when a conversion function for the underlying type to \p float was registered; + * otherwise returns 0. + * + * If \a ok is non-null: \a *ok is set to true if the value could be converted to an \p float; otherwise \a *ok is set to false. + * + * \see can_convert(), is_type() + * + * \return An float value. + */ + float to_float(bool* ok = nullptr); + + /*! + * \brief Returns the containing variant as \p double value when the type is a \p double, + * or when a conversion function for the underlying type to \p double was registered; + * otherwise returns 0. + * + * If \a ok is non-null: \a *ok is set to true if the value could be converted to an \p double; otherwise \a *ok is set to false. + * + * \see can_convert(), is_type() + * + * \return An double value. + */ + double to_double(bool* ok = nullptr); + + /*! + * \brief Creates a valid variant_array object from the underlying value when the containing type + * is an \ref type::is_array() "array" or it contains a pointer to an array type. + * + * A typical example is the following: + * + * \code{.cpp} + * int obj_array[100]; + * variant var = obj_array; // copies the content of obj_array into var + * variant_array array = var.to_array(); // Copies the content of var to a variant_array object + * auto x = array.get_size(); // set x to 100 + * array.set_value(0, 42); // set the first index to the value 42 + * \endcode + * + * \see can_convert(), convert() + * + * \return A variant_array object. + */ + variant_array to_array() const; + + + private: + template<typename T, typename SourceType> + static T get_value_with_default_value(const SourceType& source, T default_value, bool* ok); + + /*! + * \brief Returns a pointer to the underlying data + * + * \remark You do not have to use this method directly. + * + * \return + */ + void* get_ptr() const; + + /*! + * \brief Returns a pointer to the underlying data + * + * \remark You do not have to use this method directly. + * + * \return + */ + void* get_raw_ptr() const; + + /*! + * \brief Returns a pointer to the underlying data + * + * \remark You do not have to use this method directly. + * + * \return + */ + type get_raw_type() const; + + class variant_container_base + { + public: + virtual ~variant_container_base(); + + virtual type get_type() const = 0; + + virtual void* get_ptr() const = 0; + + virtual type get_raw_type() const = 0; + + virtual void* get_raw_ptr() const = 0; + + virtual bool is_array() const = 0; + + virtual detail::array_container_base* to_array() const = 0; + + virtual std::string to_string(bool* ok) const = 0; + virtual int to_int(bool* ok) const = 0; + virtual bool to_bool(bool* ok) const = 0; + virtual float to_float(bool* ok) const = 0; + virtual double to_double(bool* ok) const = 0; + + std::size_t to_size_t(bool* ok) const; + + virtual variant_container_base* clone() const = 0; + + virtual bool can_convert(const type& target_type) const = 0; + }; + + template<typename T, typename Enable = void> + class variant_container : public variant_container_base + { + public: + variant_container(const T& arg); + + variant_container(T&& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + bool can_convert(const type& target_type) const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + T _value; // the stored data + + private: // unimplemented + variant_container & operator=(const variant_container &); + }; + + template<typename T> + class variant_container<T, typename std::enable_if<detail::is_array_and_not_one_dim_char_array<T>::value>::type> : public variant_container_base + { + public: + variant_container(const T& arg); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + T _value; // the stored data + + private: // unimplemented + variant_container & operator=(const variant_container &); + }; + + template<std::size_t N> + class variant_container<char[N]> : public variant_container_base + { + public: + variant_container(const char (&arg)[N]); + + variant_container_base* clone() const; + + type get_type() const; + + void* get_ptr() const; + + type get_raw_type() const; + + void* get_raw_ptr() const; + + bool can_convert(const type& target_type) const; + + bool is_array() const; + + detail::array_container_base* to_array() const; + + std::string to_string(bool* ok) const; + int to_int(bool* ok) const; + bool to_bool(bool* ok) const; + float to_float(bool* ok) const; + double to_double(bool* ok) const; + + char _value[N]; // the stored data + + private: // unimplemented + variant_container & operator=(const variant_container &); + }; + + private: + friend variant detail::create_void_variant(); + friend detail::argument; + friend detail::instance; + variant_container_base* _holder; +}; + +#ifndef DOXYGEN +template<> RTTR_API std::string variant::convert<std::string>(bool* ok) const; +template<> RTTR_API int variant::convert<int>(bool* ok) const; +template<> RTTR_API bool variant::convert<bool>(bool* ok) const; +template<> RTTR_API float variant::convert<float>(bool* ok) const; +template<> RTTR_API double variant::convert<double>(bool* ok) const; +template<> RTTR_API variant_array variant::convert<variant_array>(bool* ok) const; + +template<> RTTR_API bool variant::can_convert<variant_array>() const; +#endif + +namespace detail +{ + RTTR_API extern variant void_variant; +} // end namespace impl + +} // end namespace rttr + +#include "rttr/impl/variant_impl.h" +#include "rttr/impl/variant_default_types_impl.h" + +#endif // __RTTR_VARIANT_H__ diff --git a/src/rttr/variant_array.cpp b/src/rttr/variant_array.cpp new file mode 100644 index 00000000..9c7a0e0e --- /dev/null +++ b/src/rttr/variant_array.cpp @@ -0,0 +1,363 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "rttr/variant_array.h" + +#include "rttr/detail/argument.h" +#include "rttr/detail/instance.h" + +using namespace std; + +namespace rttr +{ + +///////////////////////////////////////////////////////////////////////////////////////// + +variant_array::variant_array(detail::array_container_base* container) +: _container(container) +{ + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::is_valid() const +{ + return (_container ? true : false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::is_dynamic() const +{ + if (is_valid()) + return _container->is_dynamic(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +size_t variant_array::get_rank() const +{ + if (is_valid()) + return _container->get_rank(); + else + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type variant_array::get_rank_type(std::size_t index) const +{ + if (is_valid()) + return _container->get_rank_type(index); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +type variant_array::get_type() const +{ + if (is_valid()) + return _container->get_type(); + else + return impl::get_invalid_type(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::is_raw_array() const +{ + if (is_valid()) + return _container->is_raw_array(); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::size_t variant_array::get_size() const +{ + if (is_valid()) + return _container->get_size(); + else + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::size_t variant_array::get_size(std::size_t index_1) const +{ + if (is_valid()) + return _container->get_size(index_1); + else + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::size_t variant_array::get_size(std::size_t index_1, std::size_t index_2) const +{ + if (is_valid()) + return _container->get_size(index_1, index_2); + else + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +std::size_t variant_array::get_size_variadic(const std::vector<std::size_t>& index_list) const +{ + if (is_valid()) + return _container->get_size_variadic(index_list); + else + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_size(std::size_t new_size) +{ + if (is_valid()) + return _container->set_size(new_size); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_size(std::size_t new_size, std::size_t index_1) +{ + if (is_valid()) + return _container->set_size(new_size, index_1); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2) +{ + if (is_valid()) + return _container->set_size(new_size, index_1, index_2); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list) +{ + if (is_valid()) + return _container->set_size_variadic(new_size, index_list); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_value(detail::argument arg) +{ + if (is_valid()) + return _container->set_value(arg); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_value(std::size_t index_1, detail::argument arg) +{ + if (is_valid()) + return _container->set_value(arg, index_1); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_value(std::size_t index_1, std::size_t index_2, detail::argument arg) +{ + if (is_valid()) + return _container->set_value(arg, index_1, index_2); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_value(std::size_t index_1, std::size_t index_2, std::size_t index_3, detail::argument arg) +{ + if (is_valid()) + return _container->set_value( arg, index_1, index_2, index_3); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::set_value_variadic(const std::vector<std::size_t>& index_list, detail::argument arg) +{ + if (is_valid()) + return _container->set_value_variadic(arg, index_list); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant variant_array::get_value(std::size_t index_1) const +{ + if (is_valid()) + return _container->get_value(index_1); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant variant_array::get_value(std::size_t index_1, std::size_t index_2) const +{ + if (is_valid()) + return _container->get_value(index_1, index_2); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant variant_array::get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const +{ + if (is_valid()) + return _container->get_value(index_1, index_2, index_3); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +variant variant_array::get_value_variadic(const std::vector<std::size_t>& index_list) const +{ + if (is_valid()) + return _container->get_value_variadic(index_list); + else + return variant(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::insert_value(std::size_t index_1, detail::argument arg) +{ + if (is_valid()) + return _container->insert_value(arg, index_1); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::insert_value(std::size_t index_1, std::size_t index_2, detail::argument arg) +{ + if (is_valid()) + return _container->insert_value(arg, index_1, index_2); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::insert_value(std::size_t index_1, std::size_t index_2, std::size_t index_3, detail::argument arg) +{ + if (is_valid()) + return _container->insert_value(arg, index_1, index_2, index_3); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::insert_value_variadic(const std::vector<std::size_t>& index_list, detail::argument arg) +{ + if (is_valid()) + return _container->insert_value_variadic(arg, index_list); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::remove_value(std::size_t index_1) +{ + if (is_valid()) + return _container->remove_value(index_1); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::remove_value(std::size_t index_1, std::size_t index_2) +{ + if (is_valid()) + return _container->remove_value(index_1, index_2); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) +{ + if (is_valid()) + return _container->remove_value(index_1, index_2, index_3); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +bool variant_array::remove_value_variadic(const std::vector<std::size_t>& index_list) +{ + if (is_valid()) + return _container->remove_value_variadic(index_list); + else + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void* variant_array::get_ptr() const +{ + if (is_valid()) + return _container->get_ptr(); + else + return nullptr; +} + +} // end namespace rttr diff --git a/src/rttr/variant_array.h b/src/rttr/variant_array.h new file mode 100644 index 00000000..c4b6c58b --- /dev/null +++ b/src/rttr/variant_array.h @@ -0,0 +1,513 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_VARIANT_ARRAY_H__ +#define __RTTR_VARIANT_ARRAY_H__ + +#include "rttr/base/core_prerequisites.h" +#include <cstddef> +#include <vector> + +namespace rttr +{ +class variant; +class type; + +namespace detail +{ +class array_container_base; +class instance; +class argument; +} + +/*! + * The \ref variant_array class is a specialization of a \ref variant, but for array types. + * With an instance of that class you can set and get values of an array with any dimension level. + * + * A \ref variant_array can be created directly with an array type or from a \ref variant with \ref variant::to_array(). + * Use before \ref variant::is_array(), otherwise it might not be possible to create a valid variant_array object. + * + * Meta Information + * ---------------- + * An array is defined by its \ref variant_array::get_rank() "rank", it's \ref variant_array::get_size "size" and whether he is \ref variant_array::is_dynamic() "dynamic" or not. + * + * The rank of an array describes the number of dimensions. E.g. `int[10]` has a rank of `1`. `int[2][10]` has an rank of `2` and so on. + * RTTR allows you to register types with an arbitrary rank count. For retrieving the size of an array use \ref get_size(). + * With this function it is also possible to determine the size of the array relative to its rank level and it's index. + * Take a look at following example: +\code{.cpp} + std::vector<std::vector<int>> obj(10, std::vector<int>(20, 0)); + variant_array array = obj; + std::cout << array.get_size() << std::endl; // prints "10" + std::cout << array.get_size(0) << std::endl; // prints "20" + std::cout << array.get_size(1) << std::endl; // prints "20" + // INVALID call, max index is 9 + std::cout << array.get_size(10) << std::endl; // undefined behavior +\endcode + * + * When the given array type is \ref variant_array::is_dynamic() "dynamic" you can change the size of the array, therefore \ref variant_array::set_size "set_size()" should be used. + * A value of an array can be accessed with \ref variant_array::get_value "get_value()" or set with \ref variant_array::set_value "set_value". These function expect an index for up to rank level 3. + * The array class has here one interesting feature, you can set and get the value of an array up to its rank count. e.g: +\code{.cpp} + int obj[2][10]; + int sub_obj[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + array.set_value(0, sub_obj); // set the content of the obj[0] to zeros + array.set_value(0, 1, 23); // equivalent to call obj[0][1] == 23 +\endcode + * + * When you have arrays bigger then this rank, use the counterpart functions: \ref variant_array::get_value_variadic "get_value_variadic" and \ref variant_array::set_value_variadic "set_value_variadic" + * which expects a list of indices. When the array is dynamic it is also possible to \ref variant_array::insert_value "insert" or \ref variant_array::remove_value "remove" values. + * + * RTTR recognize whether a type is an array or not with the help of an `array_mapper` class template. This class does the mapping for the standard access function + * defined in the \ref array class. At the moment there exist specializations for following types: `std::array<T, N>`, `std::vector<T>`, `std::list<T>` and raw-arrays `T[N]`. + * + * When you need to bind an own custom array type, then you have the implement following functions in the class `array_mapper`. + * +\code{.cpp} +namespace rttr +{ +namespace detail +{ +template <typename T> +struct array_mapper< my_fancy_array<T> > +{ + using raw_type = typename array_mapper<T>::raw_type; + using sub_type = T; + + static bool is_dynamic() + { + ... + } + static std::size_t get_size(const my_fancy_array<T>&) + { + ... + } + static bool set_size(my_fancy_array<T>&) + { + ... + } + static const T& get_value(const my_fancy_array<T>& arr, std::size_t index) + { + ... + } + + static T& get_value(my_fancy_array<T>& arr, std::size_t index) + { + ... + } + static bool insert_value(my_fancy_array<T>&, std::size_t, const T&) + { + ... + } + static bool remove_value(my_fancy_array<T>&, std::size_t) + { + ... + } +}; +} // end namespace detail +} // end namespace rttr +\endcode + * + * Remark the namespaces rttr::detail, otherwise your specialization will not be recognized. + * + * Copying and Assignment + * ---------------------- + * A \ref variant_array object is can be copied and assigned, however each copy will perform a copy of the contained value. + * + * Typical Usage + * ---------------------- + * +\code{.cpp} + int obj[2][10]; + variant var = obj; + if (var.is_array()) + { + variant_array array = var.to_array(); + for (std::size_t index_1 = 0; index_1 < array.get_size(); ++index_1) + { + for (std::size_t index_2 = 0; index_2 < array.get_size(index_1); ++index_2) + { + array.set_value(index_1, index_2, 0); + } + } + + // it is also possible to set the sub array in one step + for (std::size_t index_1 = 0; index_1 < array.get_size(); ++index_1) + { + int zeros[10] = {0, 0, 0, 0, 0 ,0 ,0 ,0 ,0 ,0}; + array.set_value(index_1, zeros); + } + } +\endcode + * + * \see variant + */ +class RTTR_API variant_array +{ + public: + /*! + * \brief Constructs an invalid variant_array object. + * + * \see is_valid() + */ + variant_array(); + + /*! + * \brief Constructs a variant_array from the given argument \p value of type \p T. + */ + template<typename T> + variant_array(const T& value); + + /*! + * \brief Perfect forwarding of a \p value. + */ + template<typename T> + variant_array(T&& value +#ifndef DOXYGEN + , typename std::enable_if<!std::is_same<variant_array&, T>::value >::type* = 0 + , typename std::enable_if<!std::is_const<T>::value >::type* = 0 +#endif + ); + + /*! + * \brief Constructs a copy of the given variant_array \p other. + */ + variant_array(const variant_array& other); + + /*! + * \brief Constructs a new variant_array via move constructor. + */ + variant_array(variant_array&& other); + + /*! + * \brief Destroys the variant_array and the contained data. + */ + ~variant_array(); + + /*! + * \brief Returns true if this variant_array is valid, otherwise false. + * + * \return True if this array is valid, otherwise false. + */ + bool is_valid() const; + + /*! + * \brief Swaps this variant_array with the \a other variant_array. + */ + void swap(variant_array& other); + + /*! + * \brief Assigns the value of the \p other object to this variant_array. + * + * \return A reference to the variant_array with the new data. + */ + template<typename T> + variant_array& operator=(T&& other); + + /*! + * \brief Assigns the value of the \a other variant_array to this variant_array. + * + * \return A reference to the variant_array with the new data. + */ + variant_array& operator=(variant_array&& other); + + /*! + * \brief Assigns the value of the \a other variant_array to this variant_array. + * + * \return A reference to the variant_array with the new data. + */ + variant_array& operator=(const variant_array& other); + + + /*! + * \brief Returns true if this array is dynamic, that means the size can be changed; otherwise false. + * + * \see get_size() + * + * \return A boolean flag which indicates whether this array is dynamic or not. + */ + bool is_dynamic() const; + + /*! + * \brief Gets the rank (number of dimensions) of the array. + * For example, int[10] returns 1; int[2][10] returns 2, and so on. + * + * \return Returns the rank of the array. + */ + std::size_t get_rank() const; + + /*! + * \brief Gets the type of the given rank index. + * For example, an array of type int[2][10] returns for get_rank_type(0) => int[2][10] + * get_rank_type(1) => int[10]; get_rank_type(2) => int + * + * \return The rank type at the given dimension \p index. + */ + type get_rank_type(std::size_t index) const; + + /*! + * \brief Returns the \ref type object of this array. + * + * \remark When the array is not valid, this function will return an invalid type object. + * + * \return \ref type "Type" of the array. + */ + type get_type() const; + + /*! + * \brief Returns true when this array is a raw array i.e. int[10]. + * + * \return True if the array is a raw build in array, otherwise false. + */ + bool is_raw_array() const; + + /*! + * \brief Returns the size of the first dimension from the array. + * + * \return The size of the array. + */ + std::size_t get_size() const; + + /*! + * \brief Returns the size of the array at the second dimension + * at index \p index_1. + * + * \return The size of the array. + */ + std::size_t get_size(std::size_t index_1) const; + + /*! + * \brief Returns the size of the array at the third dimension at index \p index_2, + * relative to the first dimension at index \p index_1. + * + * \return The size of the array. + */ + std::size_t get_size(std::size_t index_1, std::size_t index_2) const; + + /*! + * \brief Returns the size from of the array at the specified indices in list \p index_list. + * The index count specify the array dimension. + * + * \return The size of the array. + */ + std::size_t get_size_variadic(const std::vector<std::size_t>& index_list) const; + + /*! + * \brief Sets the size of the array at the first dimension to \p new_size. + * + * \return True, when the size of the array could be changed, otherwise false. + */ + bool set_size(std::size_t new_size); + + /*! + * \brief Sets the size of the array at the second dimension + * relative to the first dimension at index \p index_1 to \p new_size. + * + * \return True, when the size of the array could be changed, otherwise false. + */ + bool set_size(std::size_t new_size, std::size_t index_1); + + /*! + * \brief Sets the size of the array at the third dimension + * relative to the first dimension at index \p index_1 + * and the second dimension at index \p index_2 to \p new_size. + * + * \return True, when the size of the array could be changed, otherwise false. + */ + bool set_size(std::size_t new_size, std::size_t index_1, std::size_t index_2); + + /*! + * \brief Sets the size of the array at the specified indices in list \p index_list. + * The index count specify the array dimension. + * + * \return True, when the size of the array could be changed, otherwise false. + */ + bool set_size_variadic(std::size_t new_size, const std::vector<std::size_t>& index_list); + + /*! + * \brief Copies the content of the the array \p arg into the underlying array. + * + * \return True if the value could be set, otherwise false. + */ + bool set_value(detail::argument arg); + + /*! + * \brief Set the content of the the argument \p arg into the in the first dimension + * of the array at index \p index_1. + * + * \return True if the value could be set, otherwise false. + */ + bool set_value(std::size_t index_1, detail::argument arg); + + /*! + * \brief Set the content of the the argument \p arg into the in the second dimension at \p index_2 + * of the array relative to the first dimension at index_1. + * + * \return True if the value could be set, otherwise false. + */ + bool set_value(std::size_t index_1, std::size_t index_2, detail::argument arg); + + /*! + * \brief Set the content of the the argument \p arg into the in the third dimension at \p index_3 + * of the array relative to the first dimension at \p index_1 and second dimension at \p index_2. + * + * \return True if the value could be set, otherwise false. + */ + bool set_value(std::size_t index_1, std::size_t index_2, std::size_t index_3, detail::argument arg); + + /*! + * \brief Set the content of the the argument \p arg into the array in the n-th dimension given in the list \p index_list. + * + * \remark Use this function when you want to set a value into a dimension which is bigger then three. + * Otherwise use the corresponding functions of \ref set_value() . + * + * \return True if the value could be set, otherwise false. + */ + bool set_value_variadic(const std::vector<std::size_t>& index_list, detail::argument arg); + + + /*! + * \brief Returns the value of the array in the first dimension at index \p index_1. + * + * \return The value of the given array at the specified indices. + */ + variant get_value(std::size_t index_1) const; + + /*! + * \brief Returns the value of the array in the second dimension at index \p index_2, + * relative to the first dimension at index \p index_1 + * + * \return The value of the given array at the specified indices. + */ + variant get_value(std::size_t index_1, std::size_t index_2) const; + + /*! + * \brief Returns the value of the array in the third dimension at index \p index_3, + * relative to the first dimension at index \p index_1 and second dimension at index \p index_2. + * + * \return The value of the given array at the specified indices. + */ + variant get_value(std::size_t index_1, std::size_t index_2, std::size_t index_3) const; + + /*! + * \brief Returns the value of the array relative to to indices given in the list \p index_list. + * + * \return The value of the given array at the specified indices. + */ + variant get_value_variadic(const std::vector<std::size_t>& index_list) const; + + /*! + * \brief Inserts the given argument \p arg into the array, in the first dimension at index \p index_1. + * + * \remark This operation is only possible when the array is \ref is_dynamic() "dynamic". + * + * \return True if \p arg could be inserted, otherwise false. + */ + bool insert_value(std::size_t index_1, detail::argument arg); + + /*! + * \brief Inserts the given argument \p arg into the array, in the second dimension at index \p index_2, + * relative to the first dimension at index \p index_1 + * + * \remark This operation is only possible when the array is \ref is_dynamic() "dynamic". + * + * \return True if \p arg could be inserted, otherwise false. + */ + bool insert_value(std::size_t index_1, std::size_t index_2, detail::argument arg); + + /*! + * \brief Inserts the given argument \p arg into the array, in the third dimension at index \p index_3, + * relative to the first dimension at index \p index_1 and the second dimension at index \p index_2. + * + * \remark This operation is only possible when the array is \ref is_dynamic() "dynamic". + * + * \return True if \p arg could be inserted, otherwise false. + */ + bool insert_value(std::size_t index_1, std::size_t index_2, std::size_t index_3, detail::argument arg); + + /*! + * \brief Inserts the given argument \p arg into the array, relative to to indices given in the list \p index_list. + * + * \remark This operation is only possible when the array is \ref is_dynamic() "dynamic". + * + * \return True if \p arg could be inserted, otherwise false. + */ + bool insert_value_variadic(const std::vector<std::size_t>& index_list, detail::argument arg); + + /*! + * \brief Removes the value at index \p index_1 in the first dimension of the array. + * + * \return True if the value could be removed, otherwise false. + */ + bool remove_value(std::size_t index_1); + + /*! + * \brief Removes the value at index \p index_2 in the second dimension of the array + * relative to the first dimension at index \p index_1. + * + * \return True if the value could be removed, otherwise false. + */ + bool remove_value(std::size_t index_1, std::size_t index_2); + + /*! + * \brief Removes the value at index \p index_3 in the third dimension of the array. + * relative to the first dimension at index \p index_1 and the second dimension at index \p index_2. + * + * \return True if the value could be removed, otherwise false. + */ + bool remove_value(std::size_t index_1, std::size_t index_2, std::size_t index_3); + + /*! + * \brief Removes the value at index \p index_1 in the first dimension of the array. + * + * \return True if the value could be removed, otherwise false. + */ + bool remove_value_variadic(const std::vector<std::size_t>& index_list); + + private: + variant_array(detail::array_container_base* container); + + /*! + * \brief Returns a pointer to the underlying data + * + * \return void pointer. + */ + void* get_ptr() const; + + private: + friend class variant; + friend class detail::argument; + detail::array_container_base* _container; +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +} // end namespace rttr + +#include "rttr/impl/variant_array_impl.h" + +#endif // __RTTR_VARIANT_ARRAY_H__ diff --git a/src/rttr/version.rc.in b/src/rttr/version.rc.in new file mode 100644 index 00000000..3a97939b --- /dev/null +++ b/src/rttr/version.rc.in @@ -0,0 +1,82 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +// version.rc.in +#define VER_FILEVERSION @RTTR_VERSION_MAJOR@,@RTTR_VERSION_MINOR@,@RTTR_VERSION_PATCH@,0 +#define VER_FILEVERSION_STR "@RTTR_VERSION_MAJOR@.@RTTR_VERSION_MINOR@.@RTTR_VERSION_PATCH@.0\0" + +#define VER_PRODUCTVERSION @RTTR_VERSION_MAJOR@,@RTTR_VERSION_MINOR@,@RTTR_VERSION_PATCH@,0 +#define VER_PRODUCTVERSION_STR "@RTTR_VERSION_MAJOR@.@RTTR_VERSION_MINOR@.@RTTR_VERSION_PATCH@.0\0" + +#ifndef DEBUG +#define VER_DEBUG 0 +#else +#define VER_DEBUG VS_FF_DEBUG +#endif + +1 VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK 0X3FL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif +FILETYPE 0X2 +FILESUBTYPE 0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", "" + VALUE "FileDescription", "" + VALUE "FileVersion", VER_FILEVERSION_STR + VALUE "InternalName", "rttr" + VALUE "LegalCopyright", "Copyright (c) 2014 Axel Menzel <info@axelmenzel.de>" + VALUE "LegalTrademarks1", "MIT License" + VALUE "LegalTrademarks2", "" + VALUE "OriginalFilename", "" + VALUE "ProductName", "@RTTR_PRODUCT_NAME@" + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + /* The following line should only be modified for localized versions. */ + /* It consists of any number of WORD,WORD pairs, with each pair */ + /* describing a language,codepage combination supported by the file. */ + /* */ + /* For example, a file might have values "0x409,1252" indicating that it */ + /* supports English language (0x409) in the Windows ANSI codepage (1252). */ + + VALUE "Translation", 0x409, 1252 + + END +END diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt new file mode 100644 index 00000000..0f39e14f --- /dev/null +++ b/src/test/CMakeLists.txt @@ -0,0 +1,66 @@ +#################################################################################### +# # +# Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> # +# # +# This file is part of RTTR (Run Time Type Reflection) # +# License: MIT License # +# # +# Permission is hereby granted, free of charge, to any person obtaining # +# a copy of this software and associated documentation files (the "Software"), # +# to deal in the Software without restriction, including without limitation # +# the rights to use, copy, modify, merge, publish, distribute, sublicense, # +# and/or sell copies of the Software, and to permit persons to whom the # +# Software is furnished to do so, subject to the following conditions: # +# # +# The above copyright notice and this permission notice shall be included in # +# all copies or substantial portions of the Software. # +# # +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # +# SOFTWARE. # +# # +#################################################################################### + +project(unit-tests) + +message(STATUS "Scanning " ${PROJECT_NAME} " module.") +message(STATUS "===========================") + +set(SOURCE_FILES main.cpp + test_type.cpp + test_variant.cpp + test_constructor_reflection.cpp + test_method_reflection.cpp + test_property_reflection.cpp + test_enumeration_reflection.cpp + ) + +set(HEADER_FILES test_classes.h + test_type.h + test_constructor_reflection.h + test_method_reflection.h + test_property_reflection.h + test_enumeration_reflection.h + catch.hpp) + +if (USE_PCH) + activate_precompiled_headers("pch.h" SOURCE_FILES) +endif() + +add_executable(unit-tests ${SOURCE_FILES} ${HEADER_FILES}) +target_link_libraries(unit-tests rttr) +add_dependencies(unit-tests rttr) + +get_target_property(UnitTest_Exec unit-tests LOCATION) +add_custom_target(run-tests ALL + COMMAND "${UnitTest_Exec}" + DEPENDS unit-tests + COMMENT "Running unit-tests") + + +message(STATUS "Scanning " ${PROJECT_NAME} " module finished!") +message(STATUS "") \ No newline at end of file diff --git a/src/test/catch.hpp b/src/test/catch.hpp new file mode 100644 index 00000000..20b25027 --- /dev/null +++ b/src/test/catch.hpp @@ -0,0 +1,8423 @@ +/* + * CATCH v1.0 build 30 (master branch) + * Generated: 2014-03-07 06:56:50.010459 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + +#define TWOBLUECUBES_CATCH_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic ignored "-Wvariadic-macros" +#pragma clang diagnostic ignored "-Wc99-extensions" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +#ifdef CATCH_CONFIG_MAIN +# define CATCH_CONFIG_RUNNER +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// #included from: internal/catch_notimplemented_exception.h +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED + +// #included from: catch_common.h +#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include <sstream> +#include <stdexcept> +#include <algorithm> + +// #included from: catch_compiler_capabilities.h +#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED + +// Much of the following code is based on Boost (1.53) + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) + +#define CATCH_CONFIG_CPP11_NULLPTR + +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#if (__BORLANDC__ > 0x582 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#if (__EDG_VERSION__ > 238 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#if (__DMC__ > 0x840 ) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ < 3 + +#if (__GNUC_MINOR__ >= 96 ) +//#define CATCH_CONFIG_SFINAE +#endif + +#elif __GNUC__ >= 3 + +// #define CATCH_CONFIG_SFINAE // Taking this out completely for now + +#endif // __GNUC__ < 3 + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) ) + +#define CATCH_CONFIG_CPP11_NULLPTR +#endif + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1310 ) // (VC++ 7.0+) +//#define CATCH_CONFIG_SFINAE // Not confirmed +#endif + +#endif // _MSC_VER + +// Use variadic macros if the compiler supports them +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) + +#ifndef CATCH_CONFIG_NO_VARIADIC_MACROS +#define CATCH_CONFIG_VARIADIC_MACROS +#endif + +#endif + +namespace Catch { + + class NonCopyable { + NonCopyable( NonCopyable const& ); + void operator = ( NonCopyable const& ); + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; + + class SafeBool { + public: + typedef void (SafeBool::*type)() const; + + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template<typename ContainerT> + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template<typename AssociativeContainerT> + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; + } + + bool startsWith( std::string const& s, std::string const& prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; + + struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); + SourceLineInfo( SourceLineInfo const& other ); + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + + std::string file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template<typename T> + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); + +#include <ostream> + +namespace Catch { + + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + + virtual ~NotImplementedException() throw() {} + + virtual const char* what() const throw(); + + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include <string> + +namespace Catch { + + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; + + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; + + IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template<typename T> + class Ptr { + public: + Ptr() : m_p( NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() { return m_p; } + const T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != NULL ); } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; + + template<typename T = IShared> + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } + + mutable unsigned int m_rc; + }; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#include <memory> +#include <vector> +#include <stdlib.h> + +namespace Catch { + + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture& getResultCapture() = 0; + virtual IRunner& getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr<IConfig const> getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr<IConfig const> const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); + +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include <vector> + +namespace Catch { + + class TestCaseFilters; + + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; + + class TestCase; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector<TestCase> const& getAllTests() const = 0; + virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const = 0; + }; +} + +namespace Catch { + +template<typename C> +class MethodTestCase : public SharedImpl<ITestCase> { + +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void(*TestFunction)(); + +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} + + const char* name; + const char* description; +}; + +struct AutoReg { + + AutoReg( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template<typename C> + AutoReg( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + registerTestCase( new MethodTestCase<C>( method ), + className, + nameAndDesc, + lineInfo ); + } + + void registerTestCase( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( ... ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + +#else + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )() + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + namespace{ \ + struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test() + +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_expression_decomposer.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_DECOMPOSER_HPP_INCLUDED + +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_expressionresult_builder.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_BUILDER_H_INCLUDED + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +// #included from: catch_sfinae.hpp +#define TWOBLUECUBES_CATCH_SFINAE_HPP_INCLUDED + +// Try to detect if the current compiler supports SFINAE + +namespace Catch { + + struct TrueType { + static const bool value = true; + typedef void Enable; + char sizer[1]; + }; + struct FalseType { + static const bool value = false; + typedef void Disable; + char sizer[2]; + }; + +#ifdef CATCH_CONFIG_SFINAE + + template<bool> struct NotABooleanExpression; + + template<bool c> struct If : NotABooleanExpression<c> {}; + template<> struct If<true> : TrueType {}; + template<> struct If<false> : FalseType {}; + + template<int size> struct SizedIf; + template<> struct SizedIf<sizeof(TrueType)> : TrueType {}; + template<> struct SizedIf<sizeof(FalseType)> : FalseType {}; + +#endif // CATCH_CONFIG_SFINAE + +} // end namespace Catch + +#include <sstream> +#include <iomanip> +#include <limits> +#include <vector> + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import <Foundation/Foundation.h> + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +namespace Catch { +namespace Detail { + +// SFINAE is currently disabled by default for all compilers. +// If the non SFINAE version of IsStreamInsertable is ambiguous for you +// and your compiler supports SFINAE, try #defining CATCH_CONFIG_SFINAE +#ifdef CATCH_CONFIG_SFINAE + + template<typename T> + class IsStreamInsertableHelper { + template<int N> struct TrueIfSizeable : TrueType {}; + + template<typename T2> + static TrueIfSizeable<sizeof((*(std::ostream*)0) << *((T2 const*)0))> dummy(T2*); + static FalseType dummy(...); + + public: + typedef SizedIf<sizeof(dummy((T*)0))> type; + }; + + template<typename T> + struct IsStreamInsertable : IsStreamInsertableHelper<T>::type {}; + +#else + + struct BorgType { + template<typename T> BorgType( T const& ); + }; + + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template<typename T> + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; + +#endif + + template<bool C> + struct StringMakerBase { + template<typename T> + static std::string convert( T const& ) { return "{?}"; } + }; + + template<> + struct StringMakerBase<true> { + template<typename T> + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + // For display purposes only. + // Does not consider endian-ness + template<typename T> + std::string rawMemoryToString( T value ) { + union { + T typedValue; + unsigned char bytes[sizeof(T)]; + }; + + typedValue = value; + + std::ostringstream oss; + oss << "0x"; + for( unsigned char* cp = bytes; cp < bytes+sizeof(T); ++cp ) + oss << std::hex << std::setw(2) << std::setfill('0') << (unsigned int)*cp; + return oss.str(); + } + +} // end namespace Detail + +template<typename T> +std::string toString( T const& value ); + +template<typename T> +struct StringMaker : + Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {}; + +template<typename T> +struct StringMaker<T*> { + template<typename U> + static std::string convert( U* p ) { + if( !p ) + return INTERNAL_CATCH_STRINGIFY( NULL ); + else + return Detail::rawMemoryToString( p ); + } +}; + +template<typename R, typename C> +struct StringMaker<R C::*> { + static std::string convert( R C::* p ) { + if( !p ) + return INTERNAL_CATCH_STRINGIFY( NULL ); + else + return Detail::rawMemoryToString( p ); + } +}; + +namespace Detail { + template<typename InputIterator> + std::string rangeToString( InputIterator first, InputIterator last ); +} + +template<typename T, typename Allocator> +struct StringMaker<std::vector<T, Allocator> > { + static std::string convert( std::vector<T,Allocator> const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); + } +}; + +namespace Detail { + template<typename T> + inline std::string makeString( T const& value ) { + return StringMaker<T>::convert( value ); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template<typename T> +std::string toString( T const& value ) { + return StringMaker<T>::convert( value ); +} + +// Built in overloads + +inline std::string toString( std::string const& value ) { + return "\"" + value + "\""; +} + +inline std::string toString( std::wstring const& value ) { + std::ostringstream oss; + oss << "\""; + for(size_t i = 0; i < value.size(); ++i ) + oss << static_cast<char>( value[i] <= 0xff ? value[i] : '?'); + oss << "\""; + return oss.str(); +} + +inline std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +inline std::string toString( char* const value ) { + return Catch::toString( static_cast<const char*>( value ) ); +} + +inline std::string toString( int value ) { + std::ostringstream oss; + oss << value; + return oss.str(); +} + +inline std::string toString( unsigned long value ) { + std::ostringstream oss; + if( value > 8192 ) + oss << "0x" << std::hex << value; + else + oss << value; + return oss.str(); +} + +inline std::string toString( unsigned int value ) { + return toString( static_cast<unsigned long>( value ) ); +} + +inline std::string toString( const double value ) { + std::ostringstream oss; + oss << std::setprecision( 10 ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +inline std::string toString( bool value ) { + return value ? "true" : "false"; +} + +inline std::string toString( char value ) { + return value < ' ' + ? toString( static_cast<unsigned int>( value ) ) + : Detail::makeString( value ); +} + +inline std::string toString( signed char value ) { + return toString( static_cast<char>( value ) ); +} + +inline std::string toString( unsigned char value ) { + return toString( static_cast<char>( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +inline std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + inline std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return std::string( "@\"" ) + [nsstring UTF8String] + "\""; + } + inline std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return std::string( "@\"" ) + [nsstring UTF8String] + "\""; + } + inline std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + + namespace Detail { + template<typename InputIterator> + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << toString( *first ); + for( ++first ; first != last ; ++first ) { + oss << ", " << toString( *first ); + } + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include <string> +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2 + + }; }; + + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + // ResultAction::Value enum + struct ResultAction { enum Value { + None, + Failed = 1, // Failure - but no debug break if Debug bit not set + Debug = 2, // If this bit is set, invoke the debugger + Abort = 4 // Test run should abort + }; }; + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x00, + + ContinueOnFailure = 0x01, // Failures fail test, but execution continues + NegateResult = 0x02, // Prefix expressiom with ! + SuppressFail = 0x04 // Failures are reported but do not fail the test + }; }; + + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) ); + } + + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool shouldNegate( int flags ) { return ( flags & ResultDisposition::NegateResult ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + + +namespace Catch { + + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + + struct AssertionResultData + { + AssertionResultData() : resultType( ResultWas::Unknown ) {} + + std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#endif + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template<Operator Op> struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits<IsEqualTo> { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits<IsNotEqualTo> { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits<IsLessThan> { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits<IsGreaterThan> { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits<IsLessThanOrEqualTo> { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } }; + + template<typename T> + inline T& opCast(T const& t) { return const_cast<T&>(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template<typename T1, typename T2, Operator Op> + class Evaluator{}; + + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsEqualTo> { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return opCast( lhs ) == opCast( rhs ); + } + }; + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsNotEqualTo> { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) != opCast( rhs ); + } + }; + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsLessThan> { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) < opCast( rhs ); + } + }; + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsGreaterThan> { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) > opCast( rhs ); + } + }; + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) >= opCast( rhs ); + } + }; + template<typename T1, typename T2> + struct Evaluator<T1, T2, IsLessThanOrEqualTo> { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return opCast( lhs ) <= opCast( rhs ); + } + }; + + template<Operator Op, typename T1, typename T2> + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator<T1, T2, Op>::evaluate( lhs, rhs ); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template<Operator Op, typename T1, typename T2> + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator<T1, T2, Op>::evaluate( lhs, rhs ); + } + + // unsigned X to int + template<Operator Op> bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); + } + template<Operator Op> bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); + } + template<Operator Op> bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) ); + } + + // unsigned X to long + template<Operator Op> bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); + } + template<Operator Op> bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); + } + template<Operator Op> bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) ); + } + + // int to unsigned X + template<Operator Op> bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); + } + template<Operator Op> bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); + } + template<Operator Op> bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs ); + } + + // long to unsigned X + template<Operator Op> bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); + } + template<Operator Op> bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); + } + template<Operator Op> bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs ); + } + + // pointer to long (when comparing against NULL) + template<Operator Op, typename T> bool compare( long lhs, T* rhs ) { + return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); + } + template<Operator Op, typename T> bool compare( T* lhs, long rhs ) { + return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); + } + + // pointer to int (when comparing against NULL) + template<Operator Op, typename T> bool compare( int lhs, T* rhs ) { + return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs ); + } + template<Operator Op, typename T> bool compare( T* lhs, int rhs ) { + return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) ); + } + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) { + return Evaluator<T*, T*, Op>::evaluate( NULL, rhs ); + } + template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) { + return Evaluator<T*, T*, Op>::evaluate( lhs, NULL ); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +namespace Catch { + +struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + +// Wraps the (stringised versions of) the lhs, operator and rhs of an expression - as well as +// the result of evaluating it. This is used to build an AssertionResult object +class ExpressionResultBuilder { +public: + + ExpressionResultBuilder( ResultWas::OfType resultType = ResultWas::Unknown ); + ExpressionResultBuilder( ExpressionResultBuilder const& other ); + ExpressionResultBuilder& operator=(ExpressionResultBuilder const& other ); + + ExpressionResultBuilder& setResultType( ResultWas::OfType result ); + ExpressionResultBuilder& setResultType( bool result ); + ExpressionResultBuilder& setLhs( std::string const& lhs ); + ExpressionResultBuilder& setRhs( std::string const& rhs ); + ExpressionResultBuilder& setOp( std::string const& op ); + + ExpressionResultBuilder& endExpression( ResultDisposition::Flags resultDisposition ); + + template<typename T> + ExpressionResultBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + std::string reconstructExpression( AssertionInfo const& info ) const; + + AssertionResult buildResult( AssertionInfo const& info ) const; + + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + +private: + AssertionResultData m_data; + struct ExprComponents { + ExprComponents() : shouldNegate( false ) {} + bool shouldNegate; + std::string lhs, rhs, op; + } m_exprComponents; + std::ostringstream m_stream; +}; + +} // end namespace Catch + +namespace Catch { + +// Wraps the LHS of an expression and captures the operator and RHS (if any) - wrapping them all +// in an ExpressionResultBuilder object +template<typename T> +class ExpressionLhs { + void operator = ( ExpressionLhs const& ); + +public: + ExpressionLhs( T lhs ) : m_lhs( lhs ) {} + + template<typename RhsT> + ExpressionResultBuilder& operator == ( RhsT const& rhs ) { + return captureExpression<Internal::IsEqualTo>( rhs ); + } + + template<typename RhsT> + ExpressionResultBuilder& operator != ( RhsT const& rhs ) { + return captureExpression<Internal::IsNotEqualTo>( rhs ); + } + + template<typename RhsT> + ExpressionResultBuilder& operator < ( RhsT const& rhs ) { + return captureExpression<Internal::IsLessThan>( rhs ); + } + + template<typename RhsT> + ExpressionResultBuilder& operator > ( RhsT const& rhs ) { + return captureExpression<Internal::IsGreaterThan>( rhs ); + } + + template<typename RhsT> + ExpressionResultBuilder& operator <= ( RhsT const& rhs ) { + return captureExpression<Internal::IsLessThanOrEqualTo>( rhs ); + } + + template<typename RhsT> + ExpressionResultBuilder& operator >= ( RhsT const& rhs ) { + return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs ); + } + + ExpressionResultBuilder& operator == ( bool rhs ) { + return captureExpression<Internal::IsEqualTo>( rhs ); + } + + ExpressionResultBuilder& operator != ( bool rhs ) { + return captureExpression<Internal::IsNotEqualTo>( rhs ); + } + + ExpressionResultBuilder& endExpression( ResultDisposition::Flags resultDisposition ) { + bool value = m_lhs ? true : false; + return m_result + .setLhs( Catch::toString( value ) ) + .setResultType( value ) + .endExpression( resultDisposition ); + } + + // Only simple binary expressions are allowed on the LHS. + // If more complex compositions are required then place the sub expression in parentheses + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( RhsT const& ); + template<typename RhsT> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( RhsT const& ); + +private: + template<Internal::Operator Op, typename RhsT> + ExpressionResultBuilder& captureExpression( RhsT const& rhs ) { + return m_result + .setResultType( Internal::compare<Op>( m_lhs, rhs ) ) + .setLhs( Catch::toString( m_lhs ) ) + .setRhs( Catch::toString( rhs ) ) + .setOp( Internal::OperatorTraits<Op>::getName() ); + } + +private: + ExpressionResultBuilder m_result; + T m_lhs; +}; + +} // end namespace Catch + +namespace Catch { + +// Captures the LHS of the expression and wraps it in an Expression Lhs object +class ExpressionDecomposer { +public: + + template<typename T> + ExpressionLhs<T const&> operator->* ( T const& operand ) { + return ExpressionLhs<T const&>( operand ); + } + + ExpressionLhs<bool> operator->* ( bool value ) { + return ExpressionLhs<bool>( value ); + } +}; + +} // end namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include <string> + +namespace Catch { + + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} + + template<typename T> + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; + }; + + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include <string> + +namespace Catch { + + class TestCase; + class ExpressionResultBuilder; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionInfo const& name, Counts const& assertions, double _durationInSeconds ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual bool shouldDebugBreak() const = 0; + + virtual ResultAction::Value acceptExpression( ExpressionResultBuilder const& assertionResult, AssertionInfo const& assertionInfo ) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + }; +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +#define CATCH_PLATFORM_IPHONE +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CATCH_PLATFORM_WINDOWS +#endif + +#include <string> + +namespace Catch{ + + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} + +#ifdef CATCH_PLATFORM_MAC + + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #ifdef DEBUG + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_BREAK_INTO_DEBUGGER() \ + if( Catch::isDebuggerActive() ) { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } + #else + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) {__asm__("int $3\n" : : );} + #endif + #endif + +#elif defined(_MSC_VER) + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { __debugbreak(); } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { DebugBreak(); } +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER +#define CATCH_BREAK_INTO_DEBUGGER() Catch::isTrue( true ); +#endif + +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include <string> + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include <iostream> +#include <string> + +namespace Catch { + + struct Verbosity { enum Level { + NoOutput = 0, + Quiet, + Normal + }; }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + }; +} + +#include <ostream> + +namespace Catch { + + inline IResultCapture& getResultCapture() { + return getCurrentContext().getResultCapture(); + } + + template<typename MatcherT> + ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, + std::string const& matcherCallAsString ) { + std::string matcherAsString = matcher.toString(); + if( matcherAsString == "{?}" ) + matcherAsString = matcherCallAsString; + return ExpressionResultBuilder() + .setRhs( matcherAsString ) + .setOp( "matches" ); + } + + template<typename MatcherT, typename ArgT> + ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, + ArgT const& arg, + std::string const& matcherCallAsString ) { + return expressionResultBuilderFromMatcher( matcher, matcherCallAsString ) + .setLhs( Catch::toString( arg ) ) + .setResultType( matcher.match( arg ) ); + } + + template<typename MatcherT, typename ArgT> + ExpressionResultBuilder expressionResultBuilderFromMatcher( MatcherT const& matcher, + ArgT* arg, + std::string const& matcherCallAsString ) { + return expressionResultBuilderFromMatcher( matcher, matcherCallAsString ) + .setLhs( Catch::toString( arg ) ) + .setResultType( matcher.match( arg ) ); + } + +struct TestFailureException{}; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ASSERTIONINFO_NAME INTERNAL_CATCH_UNIQUE_NAME( __assertionInfo ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ACCEPT_EXPR( evaluatedExpr, resultDisposition, originalExpr ) \ + if( Catch::ResultAction::Value internal_catch_action = Catch::getResultCapture().acceptExpression( evaluatedExpr, INTERNAL_CATCH_ASSERTIONINFO_NAME ) ) { \ + if( internal_catch_action & Catch::ResultAction::Debug ) CATCH_BREAK_INTO_DEBUGGER(); \ + if( internal_catch_action & Catch::ResultAction::Abort ) throw Catch::TestFailureException(); \ + if( !Catch::shouldContinueOnFailure( resultDisposition ) ) throw Catch::TestFailureException(); \ + Catch::isTrue( false && originalExpr ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ACCEPT_INFO( expr, macroName, resultDisposition ) \ + Catch::AssertionInfo INTERNAL_CATCH_ASSERTIONINFO_NAME( macroName, CATCH_INTERNAL_LINEINFO, expr, resultDisposition ); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + try { \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionDecomposer()->*expr ).endExpression( resultDisposition ), resultDisposition, expr ); \ + } catch( Catch::TestFailureException& ) { \ + throw; \ + } catch( ... ) { \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), \ + resultDisposition | Catch::ResultDisposition::ContinueOnFailure, expr ); \ + } \ + } while( Catch::isTrue( false ) ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + try { \ + expr; \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::Ok ), resultDisposition, false ); \ + } \ + catch( ... ) { \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException(), resultDisposition, false ); \ + } \ +} while( Catch::isTrue( false ) ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ + try { \ + if( Catch::getCurrentContext().getConfig()->allowThrows() ) { \ + expr; \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::DidntThrowException ), resultDisposition, false ); \ + } \ + } \ + catch( Catch::TestFailureException& ) { \ + throw; \ + } \ + catch( exceptionType ) { \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( Catch::ResultWas::Ok ), resultDisposition, false ); \ + } + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ + } while( Catch::isTrue( false ) ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( #expr, macroName, resultDisposition ); \ + INTERNAL_CATCH_THROWS_IMPL( expr, exceptionType, resultDisposition ) \ + catch( ... ) { \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ + resultDisposition | Catch::ResultDisposition::ContinueOnFailure, false ); \ + } \ + } while( Catch::isTrue( false ) ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( "", macroName, resultDisposition ); \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << __VA_ARGS__ +::Catch::StreamEndStop(), resultDisposition, true ) \ + } while( Catch::isTrue( false ) ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( "", macroName, resultDisposition ); \ + INTERNAL_CATCH_ACCEPT_EXPR( Catch::ExpressionResultBuilder( messageType ) << log, resultDisposition, true ) \ + } while( Catch::isTrue( false ) ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( log, macroName ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ + do { \ + INTERNAL_CATCH_ACCEPT_INFO( #arg " " #matcher, macroName, resultDisposition ); \ + try { \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::expressionResultBuilderFromMatcher( ::Catch::Matchers::matcher, arg, #matcher ) ), resultDisposition, false ); \ + } catch( Catch::TestFailureException& ) { \ + throw; \ + } catch( ... ) { \ + INTERNAL_CATCH_ACCEPT_EXPR( ( Catch::ExpressionResultBuilder( Catch::ResultWas::ThrewException ) << Catch::translateActiveException() ), \ + resultDisposition | Catch::ResultDisposition::ContinueOnFailure, false ); \ + } \ + } while( Catch::isTrue( false ) ) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +namespace Catch { + + struct SectionInfo { + SectionInfo( std::string const& _name, + std::string const& _description, + SourceLineInfo const& _lineInfo ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + +} // end namespace Catch + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include <cstddef> + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + return *this; + } + + std::size_t total() const { + return passed + failed; + } + + std::size_t passed; + std::size_t failed; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include <stdint.h> +#endif + +namespace Catch { + + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedNanoseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + + private: + uint64_t m_ticks; + }; + +} // namespace Catch + +#include <string> + +namespace Catch { + + class Section { + public: + Section( SourceLineInfo const& lineInfo, + std::string const& name, + std::string const& description = "" ); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool(); + + private: + + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#else + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, name, desc ) ) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include <iterator> +#include <vector> +#include <string> +#include <stdlib.h> + +namespace Catch { + +template<typename T> +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; +}; + +template<typename T> +class BetweenGenerator : public IGenerator<T> { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + + virtual T getValue( std::size_t index ) const { + return m_from+static_cast<int>( index ); + } + + virtual std::size_t size() const { + return static_cast<std::size_t>( 1+m_to-m_from ); + } + +private: + + T m_from; + T m_to; +}; + +template<typename T> +class ValuesGenerator : public IGenerator<T> { +public: + ValuesGenerator(){} + + void add( T value ) { + m_values.push_back( value ); + } + + virtual T getValue( std::size_t index ) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector<T> m_values; +}; + +template<typename T> +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) + { + move( other ); + } + + CompositeGenerator& setFileInfo( const char* fileInfo ) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll( m_composed ); + } + + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); + + typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin(); + typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end(); + for( size_t index = 0; it != itEnd; ++it ) + { + const IGenerator<T>* generator = *it; + if( overallIndex >= index && overallIndex < index + generator->size() ) + { + return generator->getValue( overallIndex-index ); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add( const IGenerator<T>* generator ) { + m_totalSize += generator->size(); + m_composed.push_back( generator ); + } + + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); + return *this; + } + + CompositeGenerator& then( T value ) { + ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); + valuesGen->add( value ); + add( valuesGen ); + return *this; + } + +private: + + void move( CompositeGenerator& other ) { + std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector<const IGenerator<T>*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators +{ + template<typename T> + CompositeGenerator<T> between( T from, T to ) { + CompositeGenerator<T> generators; + generators.add( new BetweenGenerator<T>( from, to ) ); + return generators; + } + + template<typename T> + CompositeGenerator<T> values( T val1, T val2 ) { + CompositeGenerator<T> generators; + ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } + + template<typename T> + CompositeGenerator<T> values( T val1, T val2, T val3 ){ + CompositeGenerator<T> generators; + ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } + + template<typename T> + CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator<T> generators; + ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) + +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include <string> + +namespace Catch { + + typedef std::string(*exceptionTranslateFunction)(); + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate() const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template<typename T> + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate() const { + try { + throw; + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template<typename T> + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator<T>( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\ + static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include <cmath> +#include <limits> + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits<float>::epsilon()*100 ), + m_scale( 1.0 ), + m_value( value ) + {} + + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} + + static Approx custom() { + return Approx( 0 ); + } + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.scale( m_scale ); + return approx; + } + + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + return fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( fabs(lhs), fabs(rhs.m_value) ) ); + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template<> +inline std::string toString<Detail::Approx>( Detail::Approx const& value ) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + template<typename ExpressionT> + struct Matcher : SharedImpl<IShared> + { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr<Matcher> clone() const = 0; + virtual bool match( ExpressionT const& expr ) const = 0; + virtual std::string toString() const = 0; + }; + + template<typename DerivedT, typename ExpressionT> + struct MatcherImpl : Matcher<ExpressionT> { + + virtual Ptr<Matcher<ExpressionT> > clone() const { + return Ptr<Matcher<ExpressionT> >( new DerivedT( static_cast<DerivedT const&>( *this ) ) ); + } + }; + + namespace Generic { + + template<typename ExpressionT> + class AllOf : public MatcherImpl<AllOf<ExpressionT>, ExpressionT> { + public: + + AllOf() {} + AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( Matcher<ExpressionT> const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; + }; + + template<typename ExpressionT> + class AnyOf : public MatcherImpl<AnyOf<ExpressionT>, ExpressionT> { + public: + + AnyOf() {} + AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( Matcher<ExpressionT> const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + private: + std::vector<Ptr<Matcher<ExpressionT> > > m_matchers; + }; + + } + + namespace StdString { + + inline std::string makeString( std::string const& str ) { return str; } + inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } + + struct Equals : MatcherImpl<Equals, std::string> { + Equals( std::string const& str ) : m_str( str ){} + Equals( Equals const& other ) : m_str( other.m_str ){} + + virtual ~Equals(); + + virtual bool match( std::string const& expr ) const { + return m_str == expr; + } + virtual std::string toString() const { + return "equals: \"" + m_str + "\""; + } + + std::string m_str; + }; + + struct Contains : MatcherImpl<Contains, std::string> { + Contains( std::string const& substr ) : m_substr( substr ){} + Contains( Contains const& other ) : m_substr( other.m_substr ){} + + virtual ~Contains(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + + struct StartsWith : MatcherImpl<StartsWith, std::string> { + StartsWith( std::string const& substr ) : m_substr( substr ){} + StartsWith( StartsWith const& other ) : m_substr( other.m_substr ){} + + virtual ~StartsWith(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) == 0; + } + virtual std::string toString() const { + return "starts with: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + + struct EndsWith : MatcherImpl<EndsWith, std::string> { + EndsWith( std::string const& substr ) : m_substr( substr ){} + EndsWith( EndsWith const& other ) : m_substr( other.m_substr ){} + + virtual ~EndsWith(); + + virtual bool match( std::string const& expr ) const { + return expr.find( m_substr ) == expr.size() - m_substr.size(); + } + virtual std::string toString() const { + return "ends with: \"" + m_substr + "\""; + } + + std::string m_substr; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template<typename ExpressionT> + inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1, + Impl::Matcher<ExpressionT> const& m2 ) { + return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ); + } + template<typename ExpressionT> + inline Impl::Generic::AllOf<ExpressionT> AllOf( Impl::Matcher<ExpressionT> const& m1, + Impl::Matcher<ExpressionT> const& m2, + Impl::Matcher<ExpressionT> const& m3 ) { + return Impl::Generic::AllOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 ); + } + template<typename ExpressionT> + inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1, + Impl::Matcher<ExpressionT> const& m2 ) { + return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ); + } + template<typename ExpressionT> + inline Impl::Generic::AnyOf<ExpressionT> AnyOf( Impl::Matcher<ExpressionT> const& m1, + Impl::Matcher<ExpressionT> const& m2, + Impl::Matcher<ExpressionT> const& m3 ) { + return Impl::Generic::AnyOf<ExpressionT>().add( m1 ).add( m2 ).add( m3 ); + } + + inline Impl::StdString::Equals Equals( std::string const& str ) { + return Impl::StdString::Equals( str ); + } + inline Impl::StdString::Equals Equals( const char* str ) { + return Impl::StdString::Equals( Impl::StdString::makeString( str ) ); + } + inline Impl::StdString::Contains Contains( std::string const& substr ) { + return Impl::StdString::Contains( substr ); + } + inline Impl::StdString::Contains Contains( const char* substr ) { + return Impl::StdString::Contains( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { + return Impl::StdString::StartsWith( substr ); + } + inline Impl::StdString::StartsWith StartsWith( const char* substr ) { + return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { + return Impl::StdString::EndsWith( substr ); + } + inline Impl::StdString::EndsWith EndsWith( const char* substr ) { + return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include <string> +#include <set> + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set<std::string> const& _tags, + bool _isHidden, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + std::string name; + std::string className; + std::string description; + std::set<std::string> tags; + std::string tagsAsString; + SourceLineInfo lineInfo; + bool isHidden; + }; + + class TestCase : protected TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + bool isHidden() const; + bool hasTag( std::string const& tag ) const; + bool matchesTags( std::string const& tagPattern ) const; + std::set<std::string> const& getTags() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr<ITestCase> test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: internal/catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { + class TestCase; + + struct IRunner { + virtual ~IRunner(); + }; +} + + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import <objc/runtime.h> + +#include <string> + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public SharedImpl<ITestCase> { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( NULL, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template<typename MatcherT> + struct StringHolder : MatcherImpl<MatcherT, NSString*>{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder<Equals> { + Equals( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: \"" + Catch::toString( m_substr ) + "\""; + } + }; + + struct Contains : StringHolder<Contains> { + Contains( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: \"" + Catch::toString( m_substr ) + "\""; + } + }; + + struct StartsWith : StringHolder<StartsWith> { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: \"" + Catch::toString( m_substr ) + "\""; + } + }; + struct EndsWith : StringHolder<EndsWith> { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: \"" + Catch::toString( m_substr ) + "\""; + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) + +#endif + +#ifdef CATCH_CONFIG_RUNNER +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: catch_runner.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec.h +#define TWOBLUECUBES_CATCH_TEST_SPEC_H_INCLUDED + +// #included from: catch_tags.h +#define TWOBLUECUBES_CATCH_TAGS_H_INCLUDED + +#include <string> +#include <set> +#include <map> +#include <vector> + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + class TagParser { + public: + virtual ~TagParser(); + + void parse( std::string const& str ); + + protected: + virtual void acceptTag( std::string const& tag ) = 0; + virtual void acceptChar( char c ) = 0; + virtual void endParse() {} + + private: + }; + + class TagExtracter : public TagParser { + public: + + TagExtracter( std::set<std::string>& tags ); + virtual ~TagExtracter(); + + void parse( std::string& description ); + + private: + virtual void acceptTag( std::string const& tag ); + virtual void acceptChar( char c ); + + TagExtracter& operator=(TagExtracter const&); + + std::set<std::string>& m_tags; + std::string m_remainder; + }; + + class Tag { + public: + Tag(); + Tag( std::string const& name, bool isNegated ); + std::string getName() const; + bool isNegated() const; + bool operator ! () const; + + private: + std::string m_name; + bool m_isNegated; + }; + + class TagSet { + typedef std::map<std::string, Tag> TagMap; + public: + void add( Tag const& tag ); + bool empty() const; + bool matches( std::set<std::string> const& tags ) const; + + private: + TagMap m_tags; + }; + + class TagExpression { + public: + bool matches( std::set<std::string> const& tags ) const; + + private: + friend class TagExpressionParser; + + std::vector<TagSet> m_tagSets; + }; + + class TagExpressionParser : public TagParser { + public: + TagExpressionParser( TagExpression& exp ); + ~TagExpressionParser(); + + private: + virtual void acceptTag( std::string const& tag ); + virtual void acceptChar( char c ); + virtual void endParse(); + + TagExpressionParser& operator=(TagExpressionParser const&); + + bool m_isNegated; + TagSet m_currentTagSet; + TagExpression& m_exp; + }; + +} // end namespace Catch + +#include <string> +#include <vector> + +namespace Catch { + + class TestCase; + + struct IfFilterMatches{ enum DoWhat { + AutoDetectBehaviour, + IncludeTests, + ExcludeTests + }; }; + + class TestCaseFilter { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + TestCaseFilter( std::string const& testSpec, IfFilterMatches::DoWhat matchBehaviour = IfFilterMatches::AutoDetectBehaviour ); + + IfFilterMatches::DoWhat getFilterType() const; + bool shouldInclude( TestCase const& testCase ) const; + + private: + bool isMatch( TestCase const& testCase ) const; + + std::string m_stringToMatch; + IfFilterMatches::DoWhat m_filterType; + WildcardPosition m_wildcardPosition; + }; + + class TestCaseFilters { + public: + TestCaseFilters( std::string const& name ); + std::string getName() const; + void addFilter( TestCaseFilter const& filter ); + void addTags( std::string const& tagPattern ); + bool shouldInclude( TestCase const& testCase ) const; + + private: + std::vector<TagExpression> m_tagExpressions; + std::vector<TestCaseFilter> m_inclusionFilters; + std::vector<TestCaseFilter> m_exclusionFilters; + std::string m_name; + }; + +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +#include <streambuf> + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + class Stream { + public: + Stream(); + Stream( std::streambuf* _streamBuf, bool _isOwned ); + void release(); + + std::streambuf* streamBuf; + + private: + bool isOwned; + }; +} + +#include <memory> +#include <vector> +#include <string> +#include <iostream> + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + abortAfter( -1 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + + int abortAfter; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + + std::string reporterName; + std::string outputFilename; + std::string name; + std::string processName; + + std::vector<std::string> testsOrTags; + }; + + class Config : public SharedImpl<IConfig> { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + : m_os( std::cout.rdbuf() ) + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_os( std::cout.rdbuf() ) + { + if( !data.testsOrTags.empty() ) { + std::string groupName; + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { + if( i != 0 ) + groupName += " "; + groupName += data.testsOrTags[i]; + } + TestCaseFilters filters( groupName ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) { + std::string filter = data.testsOrTags[i]; + if( startsWith( filter, "[" ) || startsWith( filter, "~[" ) ) + filters.addTags( filter ); + else + filters.addFilter( TestCaseFilter( filter ) ); + } + m_filterSets.push_back( filters ); + } + } + + virtual ~Config() { + m_os.rdbuf( std::cout.rdbuf() ); + m_stream.release(); + } + + void setFilename( std::string const& filename ) { + m_data.outputFilename = filename; + } + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { + return m_data.processName; + } + + bool shouldDebugBreak() const { + return m_data.shouldDebugBreak; + } + + void setStreamBuf( std::streambuf* buf ) { + m_os.rdbuf( buf ? buf : std::cout.rdbuf() ); + } + + void useStream( std::string const& streamName ) { + Stream stream = createStream( streamName ); + setStreamBuf( stream.streamBuf ); + m_stream.release(); + m_stream = stream; + } + + std::string getReporterName() const { return m_data.reporterName; } + + void addTestSpec( std::string const& testSpec ) { + TestCaseFilters filters( testSpec ); + filters.addFilter( TestCaseFilter( testSpec ) ); + m_filterSets.push_back( filters ); + } + + int abortAfter() const { + return m_data.abortAfter; + } + + std::vector<TestCaseFilters> const& filters() const { + return m_filterSets; + } + + bool showHelp() const { return m_data.showHelp; } + + // IConfig interface + virtual bool allowThrows() const { return !m_data.noThrow; } + virtual std::ostream& stream() const { return m_os; } + virtual std::string name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; } + + private: + ConfigData m_data; + + Stream m_stream; + mutable std::ostream m_os; + std::vector<TestCaseFilters> m_filterSets; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: clara.h + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include <string> +#include <vector> +#include <sstream> + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector<std::string>::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector<std::string> lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +#include <map> +#include <algorithm> +#include <stdexcept> +#include <memory> + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + + template<typename T> struct RemoveConstRef{ typedef T type; }; + template<typename T> struct RemoveConstRef<T&>{ typedef T type; }; + template<typename T> struct RemoveConstRef<T const&>{ typedef T type; }; + template<typename T> struct RemoveConstRef<T const>{ typedef T type; }; + + template<typename T> struct IsBool { static const bool value = false; }; + template<> struct IsBool<bool> { static const bool value = true; }; + + template<typename T> + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } + inline void convertInto( bool _source, bool& _dest ) { + _dest = _source; + } + template<typename T> + inline void convertInto( bool, T& ) { + throw std::runtime_error( "Invalid conversion" ); + } + + template<typename ConfigT> + struct IArgFunction { + virtual ~IArgFunction() {} + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual void setFlag( ConfigT& config ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template<typename ConfigT> + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( NULL ) {} + BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + void setFlag( ConfigT& config ) const { + functionObj->setFlag( config ); + } + bool takesArg() const { return functionObj->takesArg(); } + private: + IArgFunction<ConfigT>* functionObj; + }; + + template<typename C> + struct NullBinder : IArgFunction<C>{ + virtual void set( C&, std::string const& ) const {} + virtual void setFlag( C& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); } + }; + + template<typename C, typename M> + struct BoundDataMember : IArgFunction<C>{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual void setFlag( C& p ) const { + convertInto( true, p.*member ); + } + virtual bool takesArg() const { return !IsBool<M>::value; } + virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template<typename C, typename M> + struct BoundUnaryMethod : IArgFunction<C>{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef<M>::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual void setFlag( C& p ) const { + typename RemoveConstRef<M>::type value; + convertInto( true, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool<M>::value; } + virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template<typename C> + struct BoundNullaryMethod : IArgFunction<C>{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual void setFlag( C& p ) const { + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template<typename C> + struct BoundUnaryFunction : IArgFunction<C>{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual void setFlag( C& p ) const { + function( p ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template<typename C, typename T> + struct BoundBinaryFunction : IArgFunction<C>{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef<T>::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual void setFlag( C& obj ) const { + typename RemoveConstRef<T>::type value; + convertInto( true, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool<T>::value; } + virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + template<typename C, typename M> + BoundArgFunction<C> makeBoundField( M C::* _member ) { + return BoundArgFunction<C>( new BoundDataMember<C,M>( _member ) ); + } + template<typename C, typename M> + BoundArgFunction<C> makeBoundField( void (C::*_member)( M ) ) { + return BoundArgFunction<C>( new BoundUnaryMethod<C,M>( _member ) ); + } + template<typename C> + BoundArgFunction<C> makeBoundField( void (C::*_member)() ) { + return BoundArgFunction<C>( new BoundNullaryMethod<C>( _member ) ); + } + template<typename C> + BoundArgFunction<C> makeBoundField( void (*_function)( C& ) ) { + return BoundArgFunction<C>( new BoundUnaryFunction<C>( _function ) ); + } + template<typename C, typename T> + BoundArgFunction<C> makeBoundField( void (*_function)( C&, T ) ) { + return BoundArgFunction<C>( new BoundBinaryFunction<C, T>( _function ) ); + } + } // namespace Detail + + struct Parser { + Parser() : separators( " \t=:" ) {} + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const { + const std::string doubleDash = "--"; + for( int i = 1; i < argc && argv[i] != doubleDash; ++i ) + parseIntoTokens( argv[i] , tokens); + } + void parseIntoTokens( std::string arg, std::vector<Parser::Token>& tokens ) const { + while( !arg.empty() ) { + Parser::Token token( Parser::Token::Positional, arg ); + arg = ""; + if( token.data[0] == '-' ) { + if( token.data.size() > 1 && token.data[1] == '-' ) { + token = Parser::Token( Parser::Token::LongOpt, token.data.substr( 2 ) ); + } + else { + token = Parser::Token( Parser::Token::ShortOpt, token.data.substr( 1 ) ); + if( token.data.size() > 1 && separators.find( token.data[1] ) == std::string::npos ) { + arg = "-" + token.data.substr( 1 ); + token.data = token.data.substr( 0, 1 ); + } + } + } + if( token.type != Parser::Token::Positional ) { + std::size_t pos = token.data.find_first_of( separators ); + if( pos != std::string::npos ) { + arg = token.data.substr( pos+1 ); + token.data = token.data.substr( 0, pos ); + } + } + tokens.push_back( token ); + } + } + std::string separators; + }; + + template<typename ConfigT> + class CommandLine { + + struct Arg { + Arg() : position( -1 ) {} + Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ), position( -1 ) {} + + bool hasShortName( std::string const& shortName ) const { + for( std::vector<std::string>::const_iterator + it = shortNames.begin(), itEnd = shortNames.end(); + it != itEnd; + ++it ) + if( *it == shortName ) + return true; + return false; + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + bool takesArg() const { + return !placeholder.empty(); + } + bool isFixedPositional() const { + return position != -1; + } + bool isAnyPositional() const { + return position == -1 && shortNames.empty() && longName.empty(); + } + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + void validate() const { + if( boundField.takesArg() && !takesArg() ) + throw std::logic_error( "command line argument '" + dbgName() + "' must specify a placeholder" ); + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + + Detail::BoundArgFunction<ConfigT> boundField; + std::vector<std::string> shortNames; + std::string longName; + std::string description; + std::string placeholder; + int position; + }; + + // NOTE: std::auto_ptr is deprecated in c++11/c++0x +#if defined(__cplusplus) && __cplusplus > 199711L + typedef std::unique_ptr<Arg> ArgAutoPtr; +#else + typedef std::auto_ptr<Arg> ArgAutoPtr; +#endif + + class ArgBuilder { + public: + ArgBuilder( CommandLine* cl ) + : m_cl( cl ) + {} + + ArgBuilder( ArgBuilder& other ) + : m_cl( other.m_cl ), + m_arg( other.m_arg ) + { + other.m_cl = NULL; + } + // !TBD: Need to include workarounds to be able to declare this + // destructor as able to throw exceptions + ~ArgBuilder() /* noexcept(false) */ { + if( m_cl && !std::uncaught_exception() ) { + m_arg.validate(); + if( m_arg.isFixedPositional() ) { + m_cl->m_positionalArgs.insert( std::make_pair( m_arg.position, m_arg ) ); + if( m_arg.position > m_cl->m_highestSpecifiedArgPosition ) + m_cl->m_highestSpecifiedArgPosition = m_arg.position; + } + else if( m_arg.isAnyPositional() ) { + if( m_cl->m_arg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_cl->m_arg = ArgAutoPtr( new Arg( m_arg ) ); + } + else + m_cl->m_options.push_back( m_arg ); + } + } + + template<typename F> + void into( F f ) + { + m_arg.boundField = Detail::makeBoundField( f ); + } + + friend void addOptName( ArgBuilder& builder, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !builder.m_arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + builder.m_arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + builder.m_arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + builder.m_arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( ArgBuilder& builder, int position ) + { + builder.m_arg.position = position; + } + + // Can only supply placeholder after [str] - if it takes an arg + ArgBuilder& placeholder( std::string const& placeholder ) { + m_arg.placeholder = placeholder; + return *this; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg.description = description; + return *this; + } + ArgBuilder& detail( std::string const& ) { +// m_arg.description = description; +// !TBD + return *this; + } + + private: + CommandLine* m_cl; + Arg m_arg; + }; + class OptBuilder : public ArgBuilder { + public: + OptBuilder( CommandLine* cl ) : ArgBuilder( cl ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *this, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder<ConfigT>() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_arg.get() ) + m_arg = ArgAutoPtr( new Arg( *other.m_arg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + OptBuilder builder( this ); + addOptName( builder, optName ); + return builder; + } + + ArgBuilder operator[]( int position ) { + ArgBuilder builder( this ); + setPositionalArg( builder, position ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + ArgBuilder builder( this ); + return builder; + } + + template<typename F> + void bindProcessName( F f ) { + m_boundProcessName = Detail::makeBoundField( f ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + // !TBD handle longer usage strings + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth -3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage( oss ); + return oss.str(); + } + + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_arg.get() ) + os << "<" << m_arg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_arg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_arg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); + } + + void usage( std::ostream& os, std::string const& procName ) const { + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; + } + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); + } + + ConfigT parseInto( int argc, char const * const * argv ) const { + ConfigT config; + parseInto( argc, argv, config ); + return config; + } + + std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const { + std::string processName = argv[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector<Parser::Token> tokens; + Parser parser; + parser.parseIntoTokens( argc, argv, tokens ); + return populate( tokens, config ); + } + + std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { + if( m_options.empty() && m_positionalArgs.empty() ) + throw std::logic_error( "No options or arguments specified" ); + + std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } + + std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { + std::vector<Parser::Token> unusedTokens; + std::vector<std::string> errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; + + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.setFlag( config ); + } + break; + } + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + } + } + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); + } + return unusedTokens; + } + std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { + std::vector<Parser::Token> unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; + } + return unusedTokens; + } + std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const { + if( !m_arg.get() ) + return tokens; + std::vector<Parser::Token> unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_arg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + private: + Detail::BoundArgFunction<ConfigT> m_boundProcessName; + std::vector<Arg> m_options; + std::map<int, Arg> m_positionalArgs; + ArgAutoPtr m_arg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include <fstream> + +namespace Catch { + + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; + } + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = (WarnAbout::What)( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + "'" ); + + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = (Verbosity::Level)level; + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, "#" ) ) + addTestOrTags( config, line ); + } + } + + inline Clara::CommandLine<ConfigData> makeCommandLineParser() { + + using namespace Clara; + CommandLine<ConfigData> cli; + + cli.bindProcessName( &ConfigData::processName ); + + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .into( &ConfigData::showHelp ); + + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .into( &ConfigData::listTests ); + + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .into( &ConfigData::listTags ); + + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .into( &ConfigData::showSuccessfulTests ); + + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .into( &ConfigData::shouldDebugBreak ); + + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .into( &ConfigData::noThrow ); + + cli["-o"]["--out"] + .placeholder( "filename" ) + .describe( "output filename" ) + .into( &ConfigData::outputFilename ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .placeholder( "name" ) + .describe( "reporter to use (defaults to console)" ) + .into( &ConfigData::reporterName ); + + cli["-n"]["--name"] + .placeholder( "name" ) + .describe( "suite name" ) + .into( &ConfigData::name ); + + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .into( &abortAfterFirst ); + + cli["-x"]["--abortx"] + .placeholder( "number of failures" ) + .describe( "abort after x failures" ) + .into( &abortAfterX ); + + cli["-w"]["--warn"] + .placeholder( "warning name" ) + .describe( "enable warnings" ) + .into( &addWarning ); + +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); + + cli[_] + .placeholder( "test name, pattern or tags" ) + .describe( "which test or tests to use" ) + .into( &addTestOrTags ); + + cli["-d"]["--durations"] + .placeholder( "yes/no" ) + .describe( "show test durations" ) + .into( &setShowDurations ); + + cli["-f"]["--input-file"] + .placeholder( "filename" ) + .describe( "load test names to run from a file" ) + .into( &loadTestNamesFromFile ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .into( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .into( &ConfigData::listReporters ); + + return cli; + } + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include <string> +#include <vector> +#include <sstream> + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector<std::string>::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector<std::string> lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + + namespace Detail { + struct IColourImpl; + } + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + static Detail::IColourImpl* impl; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template<typename T> + class Option { + public: + Option() : nullableValue( NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != NULL; } + bool none() const { return nullableValue == NULL; } + + bool operator !() const { return nullableValue == NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +#include <string> +#include <ostream> +#include <map> +#include <assert.h> + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr<IConfig> fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr<IConfig> m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template<typename T> + struct LazyStat : Option<T> { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option<T>::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option<T>::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector<MessageInfo> const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + + AssertionResult assertionResult; + std::vector<MessageInfo> infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} + virtual ~TestRunStats(); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + }; + + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map<std::string, IReporterFactory*> FactoryMap; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + }; + +} + +#include <limits> +#include <algorithm> + +namespace Catch { + inline bool matchesFilters( std::vector<TestCaseFilters> const& filters, TestCase const& testCase ) { + std::vector<TestCaseFilters>::const_iterator it = filters.begin(); + std::vector<TestCaseFilters>::const_iterator itEnd = filters.end(); + for(; it != itEnd; ++it ) + if( !it->shouldInclude( testCase ) ) + return false; + return true; + } + + inline std::size_t listTests( Config const& config ) { + if( config.filters().empty() ) + std::cout << "All available test cases:\n"; + else + std::cout << "Matching test cases:\n"; + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); + for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); + it != itEnd; + ++it ) + if( matchesFilters( config.filters(), *it ) ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + std::cout << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + std::cout << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( config.filters().empty() ) + std::cout << pluralise( matchedTests, "test case" ) << "\n" << std::endl; + else + std::cout << pluralise( matchedTests, "matching test case" ) << "\n" << std::endl; + return matchedTests; + } + + inline std::size_t listTestsNamesOnly( Config const& config ) { + std::size_t matchedTests = 0; + std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); + for( std::vector<TestCase>::const_iterator it = allTests.begin(), itEnd = allTests.end(); + it != itEnd; + ++it ) + if( matchesFilters( config.filters(), *it ) ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + std::cout << testCaseInfo.name << std::endl; + } + return matchedTests; + } + + inline std::size_t listTags( Config const& config ) { + if( config.filters().empty() ) + std::cout << "All available tags:\n"; + else + std::cout << "Matching tags:\n"; + + std::map<std::string, int> tagCounts; + + std::vector<TestCase> const& allTests = getRegistryHub().getTestCaseRegistry().getAllTests(); + for( std::vector<TestCase>::const_iterator it = allTests.begin(), + itEnd = allTests.end(); + it != itEnd; + ++it ) { + if( matchesFilters( config.filters(), *it ) ) { + for( std::set<std::string>::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::map<std::string, int>::iterator countIt = tagCounts.find( tagName ); + if( countIt == tagCounts.end() ) + tagCounts.insert( std::make_pair( tagName, 1 ) ); + else + countIt->second++; + } + } + } + + for( std::map<std::string, int>::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << countIt->second << " "; + Text wrapper( "[" + countIt->first + "]", TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + std::cout << oss.str() << wrapper << "\n"; + } + std::cout << pluralise( tagCounts.size(), "tag" ) << "\n" << std::endl; + return tagCounts.size(); + } + + inline std::size_t listReporters( Config const& /*config*/ ) { + std::cout << "Available reports:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + std::cout << " " + << it->first + << ":" + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << "\n"; + } + std::cout << std::endl; + return factories.size(); + } + + inline Option<std::size_t> list( Config const& config ) { + Option<std::size_t> listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch + +// #included from: internal/catch_runner_impl.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include <map> +#include <string> +#include <assert.h> + +namespace Catch { +namespace SectionTracking { + + class TrackedSection { + + typedef std::map<std::string, TrackedSection> TrackedSections; + + public: + enum RunState { + NotStarted, + Executing, + ExecutingChildren, + Completed + }; + + TrackedSection( std::string const& name, TrackedSection* parent ) + : m_name( name ), m_runState( NotStarted ), m_parent( parent ) + {} + + RunState runState() const { return m_runState; } + + void addChild( std::string const& childName ) { + m_children.insert( std::make_pair( childName, TrackedSection( childName, this ) ) ); + } + TrackedSection* getChild( std::string const& childName ) { + return &m_children.find( childName )->second; + } + + void enter() { + if( m_runState == NotStarted ) + m_runState = Executing; + } + void leave() { + for( TrackedSections::const_iterator it = m_children.begin(), itEnd = m_children.end(); + it != itEnd; + ++it ) + if( it->second.runState() != Completed ) { + m_runState = ExecutingChildren; + return; + } + m_runState = Completed; + } + TrackedSection* getParent() { + return m_parent; + } + bool hasChildren() const { + return !m_children.empty(); + } + + private: + std::string m_name; + RunState m_runState; + TrackedSections m_children; + TrackedSection* m_parent; + + }; + + class TestCaseTracker { + public: + TestCaseTracker( std::string const& testCaseName ) + : m_testCase( testCaseName, NULL ), + m_currentSection( &m_testCase ), + m_completedASectionThisRun( false ) + {} + + bool enterSection( std::string const& name ) { + if( m_completedASectionThisRun ) + return false; + if( m_currentSection->runState() == TrackedSection::Executing ) { + m_currentSection->addChild( name ); + return false; + } + else { + TrackedSection* child = m_currentSection->getChild( name ); + if( child->runState() != TrackedSection::Completed ) { + m_currentSection = child; + m_currentSection->enter(); + return true; + } + return false; + } + } + void leaveSection() { + m_currentSection->leave(); + m_currentSection = m_currentSection->getParent(); + assert( m_currentSection != NULL ); + m_completedASectionThisRun = true; + } + + bool currentSectionHasChildren() const { + return m_currentSection->hasChildren(); + } + bool isCompleted() const { + return m_testCase.runState() == TrackedSection::Completed; + } + + class Guard { + public: + Guard( TestCaseTracker& tracker ) + : m_tracker( tracker ) + { + m_tracker.enterTestCase(); + } + ~Guard() { + m_tracker.leaveTestCase(); + } + private: + Guard( Guard const& ); + void operator = ( Guard const& ); + TestCaseTracker& m_tracker; + }; + + private: + void enterTestCase() { + m_currentSection = &m_testCase; + m_completedASectionThisRun = false; + m_testCase.enter(); + } + void leaveTestCase() { + m_testCase.leave(); + } + + TrackedSection m_testCase; + TrackedSection* m_currentSection; + bool m_completedASectionThisRun; + }; + +} // namespace SectionTracking + +using SectionTracking::TestCaseTracker; + +} // namespace Catch + +#include <set> +#include <string> + +namespace Catch { + + class StreamRedirect { + + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) + { + stream.rdbuf( m_oss.rdbuf() ); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); + } + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> const& reporter ) + : m_runInfo( config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( NULL ), + m_config( config ), + m_reporter( reporter ), + m_prevRunner( &m_context.getRunner() ), + m_prevResultCapture( &m_context.getResultCapture() ), + m_prevConfig( m_context.getConfig() ) + { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + m_context.setRunner( m_prevRunner ); + m_context.setConfig( NULL ); + m_context.setResultCapture( m_prevResultCapture ); + m_context.setConfig( m_prevConfig ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runMatching( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + + std::vector<TestCase> matchingTests = getRegistryHub().getTestCaseRegistry().getMatchingTestCases( testSpec ); + + Totals totals; + + testGroupStarting( testSpec, groupIndex, groupsCount ); + + std::vector<TestCase>::const_iterator it = matchingTests.begin(); + std::vector<TestCase>::const_iterator itEnd = matchingTests.end(); + for(; it != itEnd; ++it ) + totals += runTest( *it ); + + testGroupEnded( testSpec, totals, groupIndex, groupsCount ); + return totals; + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + m_testCaseTracker = TestCaseTracker( testInfo.name ); + + do { + do { + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isCompleted() && !aborting() ); + } + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = NULL; + m_testCaseTracker.reset(); + + return deltaTotals; + } + + Ptr<IConfig const> config() const { + return m_config; + } + + private: // IResultCapture + + virtual ResultAction::Value acceptExpression( ExpressionResultBuilder const& assertionResult, AssertionInfo const& assertionInfo ) { + m_lastAssertionInfo = assertionInfo; + return actOnCurrentResult( assertionResult.buildResult( assertionInfo ) ); + } + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + std::ostringstream oss; + oss << sectionInfo.name << "@" << sectionInfo.lineInfo; + + if( !m_testCaseTracker->enterSection( oss.str() ) ) + return false; + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 || + !m_config->warnAboutMissingAssertions() || + m_testCaseTracker->currentSectionHasChildren() ) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded( SectionInfo const& info, Counts const& prevAssertions, double _durationInSeconds ) { + if( std::uncaught_exception() ) { + m_unfinishedSections.push_back( UnfinishedSections( info, prevAssertions, _durationInSeconds ) ); + return; + } + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + m_testCaseTracker->leaveSection(); + + m_reporter->sectionEnded( SectionStats( info, assertions, _durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual bool shouldDebugBreak() const { + return m_config->shouldDebugBreak(); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : ""; + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() ); + } + + private: + + ResultAction::Value actOnCurrentResult( AssertionResult const& result ) { + m_lastResult = result; + assertionEnded( m_lastResult ); + + ResultAction::Value action = ResultAction::None; + + if( !m_lastResult.isOk() ) { + action = ResultAction::Failed; + if( shouldDebugBreak() ) + action = (ResultAction::Value)( action | ResultAction::Debug ); + if( aborting() ) + action = (ResultAction::Value)( action | ResultAction::Abort ); + } + return action; + } + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.name, testCaseInfo.description, testCaseInfo.lineInfo ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal ); + TestCaseTracker::Guard guard( *m_testCaseTracker ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( std::cout, redirectedCout ); + StreamRedirect cerrRedir( std::cerr, redirectedCerr ); + m_activeTestCase->invoke(); + } + else { + m_activeTestCase->invoke(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + ExpressionResultBuilder exResult( ResultWas::ThrewException ); + exResult << translateActiveException(); + actOnCurrentResult( exResult.buildResult( m_lastAssertionInfo ) ); + } + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector<UnfinishedSections>::const_iterator it = m_unfinishedSections.begin(), + itEnd = m_unfinishedSections.end(); + it != itEnd; + ++it ) + sectionEnded( it->info, it->prevAssertions, it->durationInSeconds ); + m_unfinishedSections.clear(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + + private: + struct UnfinishedSections { + UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) + : info( _info ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + + SectionInfo info; + Counts prevAssertions; + double durationInSeconds; + }; + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + Option<TestCaseTracker> m_testCaseTracker; + AssertionResult m_lastResult; + + Ptr<IConfig const> m_config; + Totals m_totals; + Ptr<IStreamingReporter> m_reporter; + std::vector<MessageInfo> m_messages; + IRunner* m_prevRunner; + IResultCapture* m_prevResultCapture; + Ptr<IConfig const> m_prevConfig; + AssertionInfo m_lastAssertionInfo; + std::vector<UnfinishedSections> m_unfinishedSections; + }; + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _buildNumber, + std::string const& _branchName ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + buildNumber( _buildNumber ), + branchName( _branchName ) + {} + + const unsigned int majorVersion; + const unsigned int minorVersion; + const unsigned int buildNumber; + const std::string branchName; + + private: + void operator=( Version const& ); + }; + + extern Version libraryVersion; +} + +#include <fstream> +#include <stdlib.h> +#include <limits> + +namespace Catch { + + class Runner { + + public: + Runner( Ptr<Config> const& config ) + : m_config( config ) + { + openStream(); + makeReporter(); + } + + Totals runTests() { + + std::vector<TestCaseFilters> filterGroups = m_config->filters(); + if( filterGroups.empty() ) { + TestCaseFilters filterGroup( "" ); + filterGroups.push_back( filterGroup ); + } + + RunContext context( m_config.get(), m_reporter ); + + Totals totals; + + for( std::size_t i=0; i < filterGroups.size() && !context.aborting(); ++i ) { + context.testGroupStarting( filterGroups[i].getName(), i, filterGroups.size() ); + totals += runTestsForGroup( context, filterGroups[i] ); + context.testGroupEnded( filterGroups[i].getName(), totals, i, filterGroups.size() ); + } + return totals; + } + + Totals runTestsForGroup( RunContext& context, const TestCaseFilters& filterGroup ) { + Totals totals; + std::vector<TestCase>::const_iterator it = getRegistryHub().getTestCaseRegistry().getAllTests().begin(); + std::vector<TestCase>::const_iterator itEnd = getRegistryHub().getTestCaseRegistry().getAllTests().end(); + int testsRunForGroup = 0; + for(; it != itEnd; ++it ) { + if( filterGroup.shouldInclude( *it ) ) { + testsRunForGroup++; + if( m_testsAlreadyRun.find( *it ) == m_testsAlreadyRun.end() ) { + + if( context.aborting() ) + break; + + totals += context.runTest( *it ); + m_testsAlreadyRun.insert( *it ); + } + } + } + if( testsRunForGroup == 0 && !filterGroup.getName().empty() ) + m_reporter->noMatchingTestCases( filterGroup.getName() ); + return totals; + + } + + private: + void openStream() { + // Open output file, if specified + if( !m_config->getFilename().empty() ) { + m_ofs.open( m_config->getFilename().c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << m_config->getFilename() << "'"; + throw std::domain_error( oss.str() ); + } + m_config->setStreamBuf( m_ofs.rdbuf() ); + } + } + void makeReporter() { + std::string reporterName = m_config->getReporterName().empty() + ? "console" + : m_config->getReporterName(); + + m_reporter = getRegistryHub().getReporterRegistry().create( reporterName, m_config.get() ); + if( !m_reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); + } + } + + private: + Ptr<Config> m_config; + std::ofstream m_ofs; + Ptr<IStreamingReporter> m_reporter; + std::set<TestCase> m_testsAlreadyRun; + }; + + class Session { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + std::cerr << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + std::cout << "\nCatch v" << libraryVersion.majorVersion << "." + << libraryVersion.minorVersion << " build " + << libraryVersion.buildNumber; + if( libraryVersion.branchName != "master" ) + std::cout << " (" << libraryVersion.branchName << " branch)"; + std::cout << "\n"; + + m_cli.usage( std::cout, processName ); + std::cout << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( argc, argv, m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + std::cerr << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( std::cout, m_configData.processName ); + return (std::numeric_limits<int>::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; + m_config.reset(); + } + + int run( int argc, char* const argv[] ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try + { + config(); // Force config to be constructed + Runner runner( m_config ); + + // Handle list request + if( Option<std::size_t> listed = list( config() ) ) + return static_cast<int>( *listed ); + + return static_cast<int>( runner.runTests().assertions.failed ); + } + catch( std::exception& ex ) { + std::cerr << ex.what() << std::endl; + return (std::numeric_limits<int>::max)(); + } + } + + Clara::CommandLine<ConfigData> const& cli() const { + return m_cli; + } + std::vector<Clara::Parser::Token> const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + + private: + Clara::CommandLine<ConfigData> m_cli; + std::vector<Clara::Parser::Token> m_unusedTokens; + ConfigData m_configData; + Ptr<Config> m_config; + }; + + bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include <vector> +#include <set> +#include <sstream> +#include <iostream> + +namespace Catch { + + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() : m_unnamedCount( 0 ) {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name == "" ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + + if( m_functions.find( testCase ) == m_functions.end() ) { + m_functions.insert( testCase ); + m_functionsInOrder.push_back( testCase ); + if( !testCase.isHidden() ) + m_nonHiddenFunctions.push_back( testCase ); + } + else { + TestCase const& prev = *m_functions.find( testCase ); + std::cerr << "error: TEST_CASE( \"" << name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << testCase.getTestCaseInfo().lineInfo << std::endl; + exit(1); + } + } + + virtual std::vector<TestCase> const& getAllTests() const { + return m_functionsInOrder; + } + + virtual std::vector<TestCase> const& getAllNonHiddenTests() const { + return m_nonHiddenFunctions; + } + + // !TBD deprecated + virtual std::vector<TestCase> getMatchingTestCases( std::string const& rawTestSpec ) const { + std::vector<TestCase> matchingTests; + getMatchingTestCases( rawTestSpec, matchingTests ); + return matchingTests; + } + + // !TBD deprecated + virtual void getMatchingTestCases( std::string const& rawTestSpec, std::vector<TestCase>& matchingTestsOut ) const { + TestCaseFilter filter( rawTestSpec ); + + std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); + std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end(); + for(; it != itEnd; ++it ) { + if( filter.shouldInclude( *it ) ) { + matchingTestsOut.push_back( *it ); + } + } + } + virtual void getMatchingTestCases( TestCaseFilters const& filters, std::vector<TestCase>& matchingTestsOut ) const { + std::vector<TestCase>::const_iterator it = m_functionsInOrder.begin(); + std::vector<TestCase>::const_iterator itEnd = m_functionsInOrder.end(); + // !TBD: replace with algorithm + for(; it != itEnd; ++it ) + if( filters.shouldInclude( *it ) ) + matchingTestsOut.push_back( *it ); + } + + private: + + std::set<TestCase> m_functions; + std::vector<TestCase> m_functionsInOrder; + std::vector<TestCase> m_nonHiddenFunctions; + size_t m_unnamedCount; + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl<ITestCase> { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, "&" ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + + /////////////////////////////////////////////////////////////////////////// + + AutoReg::AutoReg( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); + } + + AutoReg::~AutoReg() {} + + void AutoReg::registerTestCase( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + getMutableRegistryHub().registerTest + ( makeTestCase( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include <map> + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + virtual ~ReporterRegistry() { + deleteAllValues( m_factories ); + } + + virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return NULL; + return it->second->create( ReporterConfig( config ) ); + } + + void registerReporter( std::string const& name, IReporterFactory* factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + + FactoryMap const& getFactories() const { + return m_factories; + } + + private: + FactoryMap m_factories; + }; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } + + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + throw; + } + @catch (NSException *exception) { + return toString( [exception description] ); + } +#else + throw; +#endif + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return tryTranslators( m_translators.begin() ); + } + } + + std::string tryTranslators( std::vector<const IExceptionTranslator*>::const_iterator it ) const { + if( it == m_translators.end() ) + return "Unknown exception"; + + try { + return (*it)->translate(); + } + catch(...) { + return tryTranslators( it+1 ); + } + } + + private: + std::vector<const IExceptionTranslator*> m_translators; + }; +} + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, IReporterFactory* factory ) { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerTest( TestCase const& testInfo ) { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = NULL; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include <ostream> + +namespace Catch { + + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } + + const char* NotImplementedException::what() const throw() { + return m_what.c_str(); + } + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include <streambuf> + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() throw(); + }; +} + +#include <stdexcept> +#include <cstdio> + +namespace Catch { + + template<typename WriterF, size_t bufferSize=256> + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() throw() { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast<char>( c ) ) ); + else + sputc( static_cast<char>( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + Stream::Stream() + : streamBuf( NULL ), isOwned( false ) + {} + + Stream::Stream( std::streambuf* _streamBuf, bool _isOwned ) + : streamBuf( _streamBuf ), isOwned( _isOwned ) + {} + + void Stream::release() { + if( isOwned ) { + delete streamBuf; + streamBuf = NULL; + isOwned = false; + } + } +} + +namespace Catch { + + class Context : public IMutableContext { + + Context() : m_config( NULL ), m_runner( NULL ), m_resultCapture( NULL ) {} + Context( Context const& ); + void operator=( Context const& ); + + public: // IContext + virtual IResultCapture& getResultCapture() { + return *m_resultCapture; + } + virtual IRunner& getRunner() { + return *m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr<IConfig const> getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr<IConfig const> const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture().getCurrentTestName(); + + std::map<std::string, IGeneratorsForTest*>::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture().getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr<IConfig const> m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName; + }; + + namespace { + Context* currentContext = NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + Stream createStream( std::string const& streamName ) { + if( streamName == "stdout" ) return Stream( std::cout.rdbuf(), false ); + if( streamName == "stderr" ) return Stream( std::cerr.rdbuf(), false ); + if( streamName == "debug" ) return Stream( new StreamBufImpl<OutputDebugWriter>, true ); + + throw std::domain_error( "Unknown stream: " + streamName ); + } + + void cleanUpContext() { + delete currentContext; + currentContext = NULL; + } +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { namespace Detail { + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; +}} + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#ifdef __AFXDLL +#include <AfxWin.h> +#else +#include <windows.h> +#endif + +namespace Catch { +namespace { + + class Win32ColourImpl : public Detail::IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalAttributes = csbiInfo.wAttributes; + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute ); + } + HANDLE stdoutHandle; + WORD originalAttributes; + }; + + inline bool shouldUseColourForPlatform() { + return true; + } + + Win32ColourImpl platformColourImpl; + +} // end anon namespace +} // end namespace Catch + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include <unistd.h> + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public Detail::IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0:34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + private: + void setColour( const char* _escapeCode ) { + std::cout << '\033' << _escapeCode; + } + }; + + inline bool shouldUseColourForPlatform() { + return isatty(STDOUT_FILENO); + } + + PosixColourImpl platformColourImpl; + +} // end anon namespace +} // end namespace Catch + +#endif // not Windows + +namespace Catch { + + namespace { + struct NoColourImpl : Detail::IColourImpl { + void use( Colour::Code ) {} + }; + NoColourImpl noColourImpl; + static const bool shouldUseColour = shouldUseColourForPlatform() && + !isDebuggerActive(); + } + + Colour::Colour( Code _colourCode ){ use( _colourCode ); } + Colour::~Colour(){ use( None ); } + void Colour::use( Code _colourCode ) { + impl->use( _colourCode ); + } + + Detail::IColourImpl* Colour::impl = shouldUseColour + ? static_cast<Detail::IColourImpl*>( &platformColourImpl ) + : static_cast<Detail::IColourImpl*>( &noColourImpl ); + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include <vector> +#include <string> +#include <map> + +namespace Catch { + + struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} + + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin(); + std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } + return false; + } + + private: + std::map<std::string, IGeneratorInfo*> m_generatorsByName; + std::vector<IGeneratorInfo*> m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() + { + return new GeneratorsForTest(); + } + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} + + AssertionResult::AssertionResult() {} + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + AssertionResult::~AssertionResult() {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( shouldNegate( m_info.resultDisposition ) ) + return "!" + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructedExpression; + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + +} // end namespace Catch + +// #included from: catch_expressionresult_builder.hpp +#define TWOBLUECUBES_CATCH_EXPRESSIONRESULT_BUILDER_HPP_INCLUDED + +#include <assert.h> + +namespace Catch { + + ExpressionResultBuilder::ExpressionResultBuilder( ResultWas::OfType resultType ) { + m_data.resultType = resultType; + } + ExpressionResultBuilder::ExpressionResultBuilder( ExpressionResultBuilder const& other ) + : m_data( other.m_data ), + m_exprComponents( other.m_exprComponents ) + { + m_stream << other.m_stream.str(); + } + ExpressionResultBuilder& ExpressionResultBuilder::operator=(ExpressionResultBuilder const& other ) { + m_data = other.m_data; + m_exprComponents = other.m_exprComponents; + m_stream.str(""); + m_stream << other.m_stream.str(); + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::endExpression( ResultDisposition::Flags resultDisposition ) { + m_exprComponents.shouldNegate = shouldNegate( resultDisposition ); + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::setLhs( std::string const& lhs ) { + m_exprComponents.lhs = lhs; + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::setRhs( std::string const& rhs ) { + m_exprComponents.rhs = rhs; + return *this; + } + ExpressionResultBuilder& ExpressionResultBuilder::setOp( std::string const& op ) { + m_exprComponents.op = op; + return *this; + } + AssertionResult ExpressionResultBuilder::buildResult( AssertionInfo const& info ) const + { + assert( m_data.resultType != ResultWas::Unknown ); + + AssertionResultData data = m_data; + + // Flip bool results if shouldNegate is set + if( m_exprComponents.shouldNegate && data.resultType == ResultWas::Ok ) + data.resultType = ResultWas::ExpressionFailed; + else if( m_exprComponents.shouldNegate && data.resultType == ResultWas::ExpressionFailed ) + data.resultType = ResultWas::Ok; + + data.message = m_stream.str(); + data.reconstructedExpression = reconstructExpression( info ); + if( m_exprComponents.shouldNegate ) { + if( m_exprComponents.op == "" ) + data.reconstructedExpression = "!" + data.reconstructedExpression; + else + data.reconstructedExpression = "!(" + data.reconstructedExpression + ")"; + } + return AssertionResult( info, data ); + } + std::string ExpressionResultBuilder::reconstructExpression( AssertionInfo const& info ) const { + if( m_exprComponents.op == "" ) + return m_exprComponents.lhs.empty() ? info.capturedExpression : m_exprComponents.op + m_exprComponents.lhs; + else if( m_exprComponents.op == "matches" ) + return m_exprComponents.lhs + " " + m_exprComponents.rhs; + else if( m_exprComponents.op != "!" ) { + if( m_exprComponents.lhs.size() + m_exprComponents.rhs.size() < 40 && + m_exprComponents.lhs.find("\n") == std::string::npos && + m_exprComponents.rhs.find("\n") == std::string::npos ) + return m_exprComponents.lhs + " " + m_exprComponents.op + " " + m_exprComponents.rhs; + else + return m_exprComponents.lhs + "\n" + m_exprComponents.op + "\n" + m_exprComponents.rhs; + } + else + return "{can't expand - use " + info.macroName + "_FALSE( " + info.capturedExpression.substr(1) + " ) instead of " + info.macroName + "( " + info.capturedExpression + " ) for better diagnostics}"; + } + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +namespace Catch { + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + std::string desc = _descOrTags; + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + std::set<std::string> tags; + TagExtracter( tags ).parse( desc ); + if( tags.find( "hide" ) != tags.end() || tags.find( "." ) != tags.end() ) + isHidden = true; + + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + TestCaseInfo info( _name, _className, desc, tags, isHidden, _lineInfo ); + return TestCase( _testCase, info ); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set<std::string> const& _tags, + bool _isHidden, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + tags( _tags ), + lineInfo( _lineInfo ), + isHidden( _isHidden ) + { + std::ostringstream oss; + for( std::set<std::string>::const_iterator it = _tags.begin(), itEnd = _tags.end(); it != itEnd; ++it ) + oss << "[" << *it << "]"; + tagsAsString = oss.str(); + } + + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + isHidden( other.isHidden ) + {} + + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::isHidden() const { + return TestCaseInfo::isHidden; + } + + bool TestCase::hasTag( std::string const& tag ) const { + return tags.find( toLower( tag ) ) != tags.end(); + } + bool TestCase::matchesTags( std::string const& tagPattern ) const { + TagExpression exp; + TagExpressionParser( exp ).parse( tagPattern ); + return exp.matches( tags ); + } + std::set<std::string> const& TestCase::getTags() const { + return tags; + } + + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + className.swap( other.className ); + name.swap( other.name ); + description.swap( other.description ); + std::swap( lineInfo, other.lineInfo ); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch + +// #included from: catch_tags.hpp +#define TWOBLUECUBES_CATCH_TAGS_HPP_INCLUDED + +namespace Catch { + TagParser::~TagParser() {} + + void TagParser::parse( std::string const& str ) { + std::size_t pos = 0; + while( pos < str.size() ) { + char c = str[pos]; + if( c == '[' ) { + std::size_t end = str.find_first_of( ']', pos ); + if( end != std::string::npos ) { + acceptTag( str.substr( pos+1, end-pos-1 ) ); + pos = end+1; + } + else { + acceptChar( c ); + pos++; + } + } + else { + acceptChar( c ); + pos++; + } + } + endParse(); + } + + TagExtracter::TagExtracter( std::set<std::string>& tags ) + : m_tags( tags ) + {} + + TagExtracter::~TagExtracter() {} + + void TagExtracter::parse( std::string& description ) { + TagParser::parse( description ); + description = m_remainder; + } + + void TagExtracter::acceptTag( std::string const& tag ) { + m_tags.insert( toLower( tag ) ); + } + void TagExtracter::acceptChar( char c ) { + m_remainder += c; + } + + Tag::Tag() : m_isNegated( false ) {} + Tag::Tag( std::string const& name, bool isNegated ) + : m_name( name ), + m_isNegated( isNegated ) + {} + + std::string Tag::getName() const { + return m_name; + } + bool Tag::isNegated() const { + return m_isNegated; + } + + bool Tag::operator ! () const { + return m_name.empty(); + } + + void TagSet::add( Tag const& tag ) { + m_tags.insert( std::make_pair( toLower( tag.getName() ), tag ) ); + } + + bool TagSet::empty() const { + return m_tags.empty(); + } + + bool TagSet::matches( std::set<std::string> const& tags ) const { + for( TagMap::const_iterator + it = m_tags.begin(), itEnd = m_tags.end(); + it != itEnd; + ++it ) { + bool found = tags.find( it->first ) != tags.end(); + if( found == it->second.isNegated() ) + return false; + } + return true; + } + + bool TagExpression::matches( std::set<std::string> const& tags ) const { + for( std::vector<TagSet>::const_iterator + it = m_tagSets.begin(), itEnd = m_tagSets.end(); + it != itEnd; + ++it ) + if( it->matches( tags ) ) + return true; + return false; + } + + TagExpressionParser::TagExpressionParser( TagExpression& exp ) + : m_isNegated( false ), + m_exp( exp ) + {} + + TagExpressionParser::~TagExpressionParser() {} + + void TagExpressionParser::acceptTag( std::string const& tag ) { + m_currentTagSet.add( Tag( tag, m_isNegated ) ); + m_isNegated = false; + } + + void TagExpressionParser::acceptChar( char c ) { + switch( c ) { + case '~': + m_isNegated = true; + break; + case ',': + m_exp.m_tagSets.push_back( m_currentTagSet ); + m_currentTagSet = TagSet(); + break; + } + } + + void TagExpressionParser::endParse() { + if( !m_currentTagSet.empty() ) + m_exp.m_tagSets.push_back( m_currentTagSet ); + } + +} // end namespace Catch + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +namespace Catch { + + TestCaseFilter::TestCaseFilter( std::string const& testSpec, IfFilterMatches::DoWhat matchBehaviour ) + : m_stringToMatch( toLower( testSpec ) ), + m_filterType( matchBehaviour ), + m_wildcardPosition( NoWildcard ) + { + if( m_filterType == IfFilterMatches::AutoDetectBehaviour ) { + if( startsWith( m_stringToMatch, "exclude:" ) ) { + m_stringToMatch = m_stringToMatch.substr( 8 ); + m_filterType = IfFilterMatches::ExcludeTests; + } + else if( startsWith( m_stringToMatch, "~" ) ) { + m_stringToMatch = m_stringToMatch.substr( 1 ); + m_filterType = IfFilterMatches::ExcludeTests; + } + else { + m_filterType = IfFilterMatches::IncludeTests; + } + } + + if( startsWith( m_stringToMatch, "*" ) ) { + m_stringToMatch = m_stringToMatch.substr( 1 ); + m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtStart ); + } + if( endsWith( m_stringToMatch, "*" ) ) { + m_stringToMatch = m_stringToMatch.substr( 0, m_stringToMatch.size()-1 ); + m_wildcardPosition = (WildcardPosition)( m_wildcardPosition | WildcardAtEnd ); + } + } + + IfFilterMatches::DoWhat TestCaseFilter::getFilterType() const { + return m_filterType; + } + + bool TestCaseFilter::shouldInclude( TestCase const& testCase ) const { + return isMatch( testCase ) == (m_filterType == IfFilterMatches::IncludeTests); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool TestCaseFilter::isMatch( TestCase const& testCase ) const { + std::string name = testCase.getTestCaseInfo().name; + toLowerInPlace( name ); + + switch( m_wildcardPosition ) { + case NoWildcard: + return m_stringToMatch == name; + case WildcardAtStart: + return endsWith( name, m_stringToMatch ); + case WildcardAtEnd: + return startsWith( name, m_stringToMatch ); + case WildcardAtBothEnds: + return contains( name, m_stringToMatch ); + } + throw std::logic_error( "Unhandled wildcard type" ); + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + TestCaseFilters::TestCaseFilters( std::string const& name ) : m_name( name ) {} + + std::string TestCaseFilters::getName() const { + return m_name; + } + + void TestCaseFilters::addFilter( TestCaseFilter const& filter ) { + if( filter.getFilterType() == IfFilterMatches::ExcludeTests ) + m_exclusionFilters.push_back( filter ); + else + m_inclusionFilters.push_back( filter ); + } + + void TestCaseFilters::addTags( std::string const& tagPattern ) { + TagExpression exp; + TagExpressionParser( exp ).parse( tagPattern ); + + m_tagExpressions.push_back( exp ); + } + + bool TestCaseFilters::shouldInclude( TestCase const& testCase ) const { + if( !m_tagExpressions.empty() ) { + std::vector<TagExpression>::const_iterator it = m_tagExpressions.begin(); + std::vector<TagExpression>::const_iterator itEnd = m_tagExpressions.end(); + for(; it != itEnd; ++it ) + if( it->matches( testCase.getTags() ) ) + break; + if( it == itEnd ) + return false; + } + + if( !m_inclusionFilters.empty() ) { + std::vector<TestCaseFilter>::const_iterator it = m_inclusionFilters.begin(); + std::vector<TestCaseFilter>::const_iterator itEnd = m_inclusionFilters.end(); + for(; it != itEnd; ++it ) + if( it->shouldInclude( testCase ) ) + break; + if( it == itEnd ) + return false; + } + else if( m_exclusionFilters.empty() && m_tagExpressions.empty() ) { + return !testCase.isHidden(); + } + + std::vector<TestCaseFilter>::const_iterator it = m_exclusionFilters.begin(); + std::vector<TestCaseFilter>::const_iterator itEnd = m_exclusionFilters.end(); + for(; it != itEnd; ++it ) + if( !it->shouldInclude( testCase ) ) + return false; + return true; + } +} + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + // These numbers are maintained by a script + Version libraryVersion( 1, 0, 30, "master" ); +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage( m_info ); + } + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch +{ + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + + class LegacyReporterAdapter : public SharedImpl<IStreamingReporter> + { + public: + LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + + private: + Ptr<IReporter> m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ExpressionResultBuilder expressionBuilder( it->type ); + expressionBuilder << it->message; + AssertionInfo info( it->macroName, it->lineInfo, "", ResultDisposition::Normal ); + AssertionResult result = expressionBuilder.buildResult( info ); + m_legacyReporter->Result( result ); + } + } + } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#include <windows.h> +#else +#include <sys/time.h> +#endif + +namespace Catch { + + namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency((LARGE_INTEGER*)&hz); + QueryPerformanceCounter((LARGE_INTEGER*)&hzo); + } + uint64_t t; + QueryPerformanceCounter((LARGE_INTEGER*)&t); + return ((t-hzo)*1000000)/hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,NULL); + return (uint64_t)t.tv_sec * 1000000ull + (uint64_t)t.tv_usec; + } +#endif + } + + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedNanoseconds() const { + return (unsigned int)(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return (unsigned int)((getCurrentTicks() - m_ticks)/1000); + } + double Timer::getElapsedSeconds() const { + return (getCurrentTicks() - m_ticks)/1000000.0; + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && s.substr( 0, prefix.size() ) == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && s.substr( s.size()-suffix.size(), suffix.size() ) == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), ::tolower ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << " " << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << "s"; + return os; + } + + SourceLineInfo::SourceLineInfo() : line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + SourceLineInfo::SourceLineInfo( SourceLineInfo const& other ) + : file( other.file ), + line( other.line ) + {} + bool SourceLineInfo::empty() const { + return file.empty(); + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && file == other.file; + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << "(" << info.line << ")"; +#else + os << info.file << ":" << info.line; +#endif + return os; + } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << "'"; + if( isTrue( true )) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + Section::Section( SourceLineInfo const& lineInfo, + std::string const& name, + std::string const& description ) + : m_info( name, description, lineInfo ), + m_sectionIncluded( getCurrentContext().getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) + getCurrentContext().getResultCapture().sectionEnded( m_info, m_assertions, m_timer.getElapsedSeconds() ); + } + + // This indicates whether the section should be executed or not + Section::operator bool() { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#include <iostream> + +#ifdef CATCH_PLATFORM_MAC + + #include <assert.h> + #include <stdbool.h> + #include <sys/types.h> + #include <unistd.h> + #include <sys/sysctl.h> + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0) != 0 ) { + std::cerr << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA( const char* ); + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + std::cout << text; + } + } +#endif // Platform + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +namespace Catch { + + struct StreamingReporterBase : SharedImpl<IStreamingReporter> { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + + virtual ~StreamingReporterBase(); + + virtual void noMatchingTestCases( std::string const& ) {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { + currentTestCaseInfo.reset(); + assert( m_sectionStack.empty() ); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + Ptr<IConfig> m_config; + std::ostream& stream; + + LazyStat<TestRunInfo> currentTestRunInfo; + LazyStat<GroupInfo> currentGroupInfo; + LazyStat<TestCaseInfo> currentTestCaseInfo; + + std::vector<SectionInfo> m_sectionStack; + }; + + struct CumulativeReporterBase : SharedImpl<IStreamingReporter> { + template<typename T, typename ChildNodeT> + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector<Ptr<ChildNodeT> > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr<SectionNode> const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector<Ptr<SectionNode> > ChildSections; + typedef std::vector<AssertionStats> Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + bool operator() ( Ptr<SectionNode> const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + BySectionInfo& operator=( BySectionInfo const& other ); // = delete; + + SectionInfo const& m_other; + }; + + typedef Node<TestCaseStats, SectionNode> TestCaseNode; + typedef Node<TestGroupStats, TestCaseNode> TestGroupNode; + typedef Node<TestRunStats, TestGroupNode> TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + {} + ~CumulativeReporterBase(); + + virtual void testRunStarting( TestRunInfo const& ) {} + virtual void testGroupStarting( GroupInfo const& ) {} + + virtual void testCaseStarting( TestCaseInfo const& ) {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr<SectionNode> node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) { + Ptr<TestRunNode> node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + Ptr<IConfig> m_config; + std::ostream& stream; + std::vector<AssertionStats> m_assertions; + std::vector<std::vector<Ptr<SectionNode> > > m_sections; + std::vector<Ptr<TestCaseNode> > m_testCases; + std::vector<Ptr<TestGroupNode> > m_testGroups; + + std::vector<Ptr<TestRunNode> > m_testRuns; + + Ptr<SectionNode> m_rootSection; + Ptr<SectionNode> m_deepestSection; + std::vector<Ptr<SectionNode> > m_sectionStack; + + }; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + + template<typename T> + class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template<typename T> + class ReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include <sstream> +#include <iostream> +#include <string> +#include <vector> + +namespace Catch { + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = NULL; + } + + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } + + template<typename T> + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &std::cout ) + {} + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( &os ) + {} + + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& operator = ( XmlWriter const& other ) { + XmlWriter temp( other ); + swap( temp ); + return *this; + } + + void swap( XmlWriter& other ) { + std::swap( m_tagIsOpen, other.m_tagIsOpen ); + std::swap( m_needsNewline, other.m_needsNewline ); + std::swap( m_tags, other.m_tags ); + std::swap( m_indent, other.m_indent ); + std::swap( m_os, other.m_os ); + } + + XmlWriter& startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + stream() << m_indent << "<" << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + stream() << "/>\n"; + m_tagIsOpen = false; + } + else { + stream() << m_indent << "</" << m_tags.back() << ">\n"; + } + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) { + stream() << " " << name << "=\""; + writeEncodedText( attribute ); + stream() << "\""; + } + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + stream() << " " << name << "=\"" << ( attribute ? "true" : "false" ) << "\""; + return *this; + } + + template<typename T> + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + if( !name.empty() ) + stream() << " " << name << "=\"" << attribute << "\""; + return *this; + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + stream() << m_indent; + writeEncodedText( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + stream() << m_indent << "<!--" << text << "-->"; + m_needsNewline = true; + return *this; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + stream() << "\n"; + return *this; + } + + private: + + std::ostream& stream() { + return *m_os; + } + + void ensureTagClosed() { + if( m_tagIsOpen ) { + stream() << ">\n"; + m_tagIsOpen = false; + } + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + stream() << "\n"; + m_needsNewline = false; + } + } + + void writeEncodedText( std::string const& text ) { + static const char* charsToEncode = "<&\""; + std::string mtext = text; + std::string::size_type pos = mtext.find_first_of( charsToEncode ); + while( pos != std::string::npos ) { + stream() << mtext.substr( 0, pos ); + + switch( mtext[pos] ) { + case '<': + stream() << "<"; + break; + case '&': + stream() << "&"; + break; + case '\"': + stream() << """; + break; + } + mtext = mtext.substr( pos+1 ); + pos = mtext.find_first_of( charsToEncode ); + } + stream() << mtext; + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector<std::string> m_tags; + std::string m_indent; + std::ostream* m_os; + }; + +} +namespace Catch { + class XmlReporter : public SharedImpl<IReporter> { + public: + XmlReporter( ReporterConfig const& config ) : m_config( config ), m_sectionDepth( 0 ) {} + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + virtual ~XmlReporter(); + + private: // IReporter + + virtual bool shouldRedirectStdout() const { + return true; + } + + virtual void StartTesting() { + m_xml = XmlWriter( m_config.stream() ); + m_xml.startElement( "Catch" ); + if( !m_config.fullConfig()->name().empty() ) + m_xml.writeAttribute( "name", m_config.fullConfig()->name() ); + } + + virtual void EndTesting( const Totals& totals ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", totals.assertions.passed ) + .writeAttribute( "failures", totals.assertions.failed ); + m_xml.endElement(); + } + + virtual void StartGroup( const std::string& groupName ) { + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupName ); + } + + virtual void EndGroup( const std::string&, const Totals& totals ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", totals.assertions.passed ) + .writeAttribute( "failures", totals.assertions.failed ); + m_xml.endElement(); + } + + virtual void StartSection( const std::string& sectionName, const std::string& description ) { + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionName ) ) + .writeAttribute( "description", description ); + } + } + virtual void NoAssertionsInSection( const std::string& ) {} + virtual void NoAssertionsInTestCase( const std::string& ) {} + + virtual void EndSection( const std::string& /*sectionName*/, const Counts& assertions ) { + if( --m_sectionDepth > 0 ) { + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", assertions.passed ) + .writeAttribute( "failures", assertions.failed ); + m_xml.endElement(); + } + } + + virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) { + m_xml.startElement( "TestCase" ).writeAttribute( "name", trim( testInfo.name ) ); + m_currentTestSuccess = true; + } + + virtual void Result( const Catch::AssertionResult& assertionResult ) { + if( !m_config.fullConfig()->includeSuccessfulResults() && assertionResult.getResultType() == ResultWas::Ok ) + return; + + if( assertionResult.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", assertionResult.succeeded() ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ); + + m_xml.scopedElement( "Original" ) + .writeText( assertionResult.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( assertionResult.getExpandedExpression() ); + m_currentTestSuccess &= assertionResult.succeeded(); + } + + switch( assertionResult.getResultType() ) { + case ResultWas::ThrewException: + m_xml.scopedElement( "Exception" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + m_currentTestSuccess = false; + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Warning: + m_xml.scopedElement( "Warning" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement( "Failure" ) + .writeText( assertionResult.getMessage() ); + m_currentTestSuccess = false; + break; + case ResultWas::Unknown: + case ResultWas::Ok: + case ResultWas::FailureBit: + case ResultWas::ExpressionFailed: + case ResultWas::Exception: + case ResultWas::DidntThrowException: + break; + } + if( assertionResult.hasExpression() ) + m_xml.endElement(); + } + + virtual void Aborted() { + // !TBD + } + + virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string&, const std::string& ) { + m_xml.scopedElement( "OverallResult" ).writeAttribute( "success", m_currentTestSuccess ); + m_xml.endElement(); + } + + private: + ReporterConfig m_config; + bool m_currentTestSuccess; + XmlWriter m_xml; + int m_sectionDepth; + }; + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include <assert.h> + +namespace Catch { + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + {} + + ~JunitReporter(); + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) {} + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = true; + return prefs; + } + + virtual void testRunStarting( TestRunInfo const& runInfo ) { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", "tbd" ); // !TBD + + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; + } + writeSection( className, "", rootSection ); + } + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + "/" + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << "\n"; + for( std::vector<MessageInfo>::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << "\n"; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +namespace Catch { + + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ), + m_atLeastOneTestCasePrinted( false ) + {} + + virtual ~ConsoleReporter(); + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << "'" << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_headerPrinted ) { + if( m_config->showDurations() == ShowDurations::Always ) + stream << "Completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + m_headerPrinted = false; + } + else { + if( m_config->showDurations() == ShowDurations::Always ) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << "s" << std::endl; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) { + StreamingReporterBase::testCaseEnded( _testCaseStats ); + m_headerPrinted = false; + } + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << "\n" << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + if( m_atLeastOneTestCasePrinted ) + printTotalsDivider(); + printTotals( _testRunStats.totals ); + stream << "\n" << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) + { + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << "\n"; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } + else { + stream << "\n"; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << "\n"; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << "\n"; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ":" << "\n"; + for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << "\n"; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector<MessageInfo> messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + m_atLeastOneTestCasePrinted = true; + } + void lazyPrintRunInfo() { + stream << "\n" << getTildes() << "\n"; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion.majorVersion << "." + << libraryVersion.minorVersion << " b" + << libraryVersion.buildNumber; + if( libraryVersion.branchName != "master" ) + stream << " (" << libraryVersion.branchName << ")"; + stream << " host application.\n" + << "Run with -? for options\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector<SectionInfo>::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.front().lineInfo; + + if( !lineInfo.empty() ){ + stream << getDashes() << "\n"; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << "\n"; + } + stream << getDots() << "\n" << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getDots() << "\n"; + } + void printOpenHeader( std::string const& _name ) { + stream << getDashes() << "\n"; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; + else + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << "\n"; + } + + void printTotals( const Totals& totals ) { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran"; + } + else if( totals.assertions.total() == 0 ) { + Colour colour( Colour::Yellow ); + printCounts( "test case", totals.testCases ); + stream << " (no assertions)"; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + printCounts( "test case", totals.testCases ); + if( totals.testCases.failed > 0 ) { + stream << " ("; + printCounts( "assertion", totals.assertions ); + stream << ")"; + } + } + else { + Colour colour( Colour::ResultSuccess ); + stream << "All tests passed (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ")"; + } + } + void printCounts( std::string const& label, Counts const& counts ) { + if( counts.total() == 1 ) { + stream << "1 " << label << " - "; + if( counts.failed ) + stream << "failed"; + else + stream << "passed"; + } + else { + stream << counts.total() << " " << label << "s "; + if( counts.passed ) { + if( counts.failed ) + stream << "- " << counts.failed << " failed"; + else if( counts.passed == 2 ) + stream << "- both passed"; + else + stream << "- all passed"; + } + else { + if( counts.failed == 2 ) + stream << "- both failed"; + else + stream << "- all failed"; + } + } + } + + void printTotalsDivider() { + stream << getDoubleDashes() << "\n"; + } + void printSummaryDivider() { + stream << getDashes() << "\n"; + } + static std::string const& getDashes() { + static const std::string dashes( CATCH_CONFIG_CONSOLE_WIDTH-1, '-' ); + return dashes; + } + static std::string const& getDots() { + static const std::string dots( CATCH_CONFIG_CONSOLE_WIDTH-1, '.' ); + return dots; + } + static std::string const& getDoubleDashes() { + static const std::string doubleDashes( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + return doubleDashes; + } + static std::string const& getTildes() { + static const std::string dots( CATCH_CONFIG_CONSOLE_WIDTH-1, '~' ); + return dots; + } + + private: + bool m_headerPrinted; + bool m_atLeastOneTestCasePrinted; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + +} // end namespace Catch + +namespace Catch { + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + StreamBufBase::~StreamBufBase() throw() {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} + + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + + void Config::dummy() {} + + INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main (int argc, char * const argv[]) { + return Catch::Session().run( argc, argv ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char* const*)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return result; +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::NegateResult, "CATCH_REQUIRE_FALSE" ) + +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS" ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) + +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::NegateResult, "CATCH_CHECK_FALSE" ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) + +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#else + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#else +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#endif +#define CATCH_GIVEN( desc ) CATCH_SECTION( "Given: " desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( " When: " desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( " And: " desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( " Then: " desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( " And: " desc, "" ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::NegateResult, "REQUIRE_FALSE" ) + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::Normal, "REQUIRE_THROWS" ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) + +#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::NegateResult, "CHECK_FALSE" ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) + +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, ..., Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS" ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) + +#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#else + #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#else +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#endif +#define GIVEN( desc ) SECTION( " Given: " desc, "" ) +#define WHEN( desc ) SECTION( " When: " desc, "" ) +#define AND_WHEN( desc ) SECTION( "And when: " desc, "" ) +#define THEN( desc ) SECTION( " Then: " desc, "" ) +#define AND_THEN( desc ) SECTION( " And: " desc, "" ) + +using Catch::Detail::Approx; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/src/test/main.cpp b/src/test/main.cpp new file mode 100644 index 00000000..4a6bce09 --- /dev/null +++ b/src/test/main.cpp @@ -0,0 +1,29 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#include "catch.hpp" diff --git a/src/test/pch.h b/src/test/pch.h new file mode 100644 index 00000000..ed29cc63 --- /dev/null +++ b/src/test/pch.h @@ -0,0 +1,20 @@ +// std stuff +#include <map> +#include <string> +#include <vector> +#include <set> +#include <list> +#include <iostream> +#include <limits> +#include <sstream> +#include <fstream> +#include <iomanip> +#include <tuple> +#include <algorithm> +#include <cassert> +#include <climits> +#include <iosfwd> +#include <memory> +#include <type_traits> + +#include <chrono> diff --git a/src/test/test_classes.h b/src/test/test_classes.h new file mode 100644 index 00000000..e6474234 --- /dev/null +++ b/src/test/test_classes.h @@ -0,0 +1,189 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#ifndef __RTTR_TESTCLASSES_H__ +#define __RTTR_TESTCLASSES_H__ + +#include <rttr/type> + +#define CLASS(CLASS_NAME) struct CLASS_NAME { RTTR_ENABLE() virtual int getType() { return 0; } int dummyIntValue; }; \ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS_NAME) + +#define CLASS_INHERIT(CLASS1, CLASS2) struct CLASS1 : CLASS2 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2) double dummyDoubleValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_MULTI_INHERIT_2(CLASS1, CLASS2, CLASS3) struct CLASS1 : CLASS2, CLASS3 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2, CLASS3) bool dummyBoolValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +#define CLASS_MULTI_INHERIT_5(CLASS1, CLASS2, CLASS3, CLASS4, CLASS5, CLASS6) struct CLASS1 : CLASS2, CLASS3, CLASS4, CLASS5, CLASS6 { virtual int getType() { return 1; } RTTR_ENABLE(CLASS2, CLASS3, CLASS4, CLASS5, CLASS6) bool dummyBoolValue; };\ + RTTR_DECLARE_STANDARD_TYPE_VARIANTS(CLASS1) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and is 5 classes wide; +// only single inheritance +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassSingleBase) +CLASS_INHERIT(ClassSingle1A, ClassSingleBase) +CLASS_INHERIT(ClassSingle2A, ClassSingle1A) +CLASS_INHERIT(ClassSingle3A, ClassSingle2A) +CLASS_INHERIT(ClassSingle4A, ClassSingle3A) +CLASS_INHERIT(ClassSingle5A, ClassSingle4A) +CLASS_INHERIT(ClassSingle6A, ClassSingle5A) + +CLASS_INHERIT(ClassSingle1B, ClassSingleBase) +CLASS_INHERIT(ClassSingle2B, ClassSingle1B) +CLASS_INHERIT(ClassSingle3B, ClassSingle2B) +CLASS_INHERIT(ClassSingle4B, ClassSingle3B) +CLASS_INHERIT(ClassSingle5B, ClassSingle4B) +CLASS_INHERIT(ClassSingle6B, ClassSingle5B) + +CLASS_INHERIT(ClassSingle1C, ClassSingleBase) +CLASS_INHERIT(ClassSingle2C, ClassSingle1C) +CLASS_INHERIT(ClassSingle3C, ClassSingle2C) +CLASS_INHERIT(ClassSingle4C, ClassSingle3C) +CLASS_INHERIT(ClassSingle5C, ClassSingle4C) +CLASS_INHERIT(ClassSingle6C, ClassSingle5C) + +CLASS_INHERIT(ClassSingle1D, ClassSingleBase) +CLASS_INHERIT(ClassSingle2D, ClassSingle1D) +CLASS_INHERIT(ClassSingle3D, ClassSingle2D) +CLASS_INHERIT(ClassSingle4D, ClassSingle3D) +CLASS_INHERIT(ClassSingle5D, ClassSingle4D) +CLASS_INHERIT(ClassSingle6D, ClassSingle5D) + +CLASS_INHERIT(ClassSingle1E, ClassSingleBase) +CLASS_INHERIT(ClassSingle2E, ClassSingle1E) +CLASS_INHERIT(ClassSingle3E, ClassSingle2E) +CLASS_INHERIT(ClassSingle4E, ClassSingle3E) +CLASS_INHERIT(ClassSingle5E, ClassSingle4E) +CLASS_INHERIT(ClassSingle6E, ClassSingle5E) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and 5 classes wide; +// At the end is a final class which uses multiple inheritance to combine all classes +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassMultipleBaseA) +CLASS_INHERIT(ClassMultiple1A, ClassMultipleBaseA) +CLASS_INHERIT(ClassMultiple2A, ClassMultiple1A) +CLASS_INHERIT(ClassMultiple3A, ClassMultiple2A) +CLASS_INHERIT(ClassMultiple4A, ClassMultiple3A) +CLASS_INHERIT(ClassMultiple5A, ClassMultiple4A) +CLASS_INHERIT(ClassMultiple6A, ClassMultiple5A) + +CLASS(ClassMultipleBaseB) +CLASS_INHERIT(ClassMultiple1B, ClassMultipleBaseB) +CLASS_INHERIT(ClassMultiple2B, ClassMultiple1B) +CLASS_INHERIT(ClassMultiple3B, ClassMultiple2B) +CLASS_INHERIT(ClassMultiple4B, ClassMultiple3B) +CLASS_INHERIT(ClassMultiple5B, ClassMultiple4B) +CLASS_INHERIT(ClassMultiple6B, ClassMultiple5B) + +CLASS(ClassMultipleBaseC) +CLASS_INHERIT(ClassMultiple1C, ClassMultipleBaseC) +CLASS_INHERIT(ClassMultiple2C, ClassMultiple1C) +CLASS_INHERIT(ClassMultiple3C, ClassMultiple2C) +CLASS_INHERIT(ClassMultiple4C, ClassMultiple3C) +CLASS_INHERIT(ClassMultiple5C, ClassMultiple4C) +CLASS_INHERIT(ClassMultiple6C, ClassMultiple5C) + +CLASS(ClassMultipleBaseD) +CLASS_INHERIT(ClassMultiple1D, ClassMultipleBaseD) +CLASS_INHERIT(ClassMultiple2D, ClassMultiple1D) +CLASS_INHERIT(ClassMultiple3D, ClassMultiple2D) +CLASS_INHERIT(ClassMultiple4D, ClassMultiple3D) +CLASS_INHERIT(ClassMultiple5D, ClassMultiple4D) +CLASS_INHERIT(ClassMultiple6D, ClassMultiple5D) + +CLASS(ClassMultipleBaseE) +CLASS_INHERIT(ClassMultiple1E, ClassMultipleBaseE) +CLASS_INHERIT(ClassMultiple2E, ClassMultiple1E) +CLASS_INHERIT(ClassMultiple3E, ClassMultiple2E) +CLASS_INHERIT(ClassMultiple4E, ClassMultiple3E) +CLASS_INHERIT(ClassMultiple5E, ClassMultiple4E) +CLASS_INHERIT(ClassMultiple6E, ClassMultiple5E) + +CLASS_MULTI_INHERIT_5(FinalClass, ClassMultiple6A, ClassMultiple6B, ClassMultiple6C, ClassMultiple6D, ClassMultiple6E) + + +///////////////////////////////////////////////////////////////////////////////////////// +// The diamond problem +///////////////////////////////////////////////////////////////////////////////////////// +struct DiamondTop +{ + + double foo = 12; + RTTR_ENABLE() +}; + +struct DiamondLeft : virtual DiamondTop +{ + bool _left_var = true; + RTTR_ENABLE(DiamondTop) +}; + +struct DiamondRight : virtual DiamondTop +{ + std::string _text = "Hello World"; + RTTR_ENABLE(DiamondTop) +}; + + +struct DiamondBottom : DiamondLeft, DiamondRight +{ + int _finalVar = 42; + RTTR_ENABLE(DiamondLeft, DiamondRight) +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(DiamondTop) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(DiamondLeft) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(DiamondRight) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(DiamondBottom) + +///////////////////////////////////////////////////////////////////////////////////////// +// The following class structures has 7 hierarchy levels and 2 classes wide; +// At the end is a final class which uses multiple inheritance to combine all classes +///////////////////////////////////////////////////////////////////////////////////////// + +CLASS(ClassMulti1A) + +CLASS(ClassMulti1B) +CLASS(ClassMulti2B) +CLASS(ClassMulti3B) +CLASS(ClassMulti4B) +CLASS(ClassMulti5B) +CLASS(ClassMulti6B) + +CLASS_MULTI_INHERIT_2(ClassMulti2A, ClassMulti1A, ClassMulti1B) +CLASS_MULTI_INHERIT_2(ClassMulti3A, ClassMulti2A, ClassMulti2B) +CLASS_MULTI_INHERIT_2(ClassMulti4A, ClassMulti3A, ClassMulti3B) +CLASS_MULTI_INHERIT_2(ClassMulti5A, ClassMulti4A, ClassMulti4B) +CLASS_MULTI_INHERIT_2(ClassMulti6A, ClassMulti5A, ClassMulti5B) +CLASS_MULTI_INHERIT_2(ClassMulti7A, ClassMulti6A, ClassMulti6B) + +#endif // __RTTR_TESTCLASSES_H__ diff --git a/src/test/test_constructor_reflection.cpp b/src/test/test_constructor_reflection.cpp new file mode 100644 index 00000000..a8f67c88 --- /dev/null +++ b/src/test/test_constructor_reflection.cpp @@ -0,0 +1,180 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_constructor_reflection.h" + +#include <rttr/reflect> + +using namespace rttr; +using namespace std; + +#include <iostream> +#include <memory> +#include <functional> + +#include "catch.hpp" + +enum E_MetaData +{ + SCRIPTABLE = 0, + TOOL_TIP = 1, + DESCRIPTION = 2 +}; + +RTTR_REGISTER +{ + class_<constructor_test>() + .constructor<>() + .constructor<int, int>() + .constructor<const std::string&>({metadata("SCRIPTABLE", true), metadata(TOOL_TIP, string("This is a ToolTip"))}) + .constructor<int, int, int, int, int, int, const int* const>(); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test constructor/destructor", "[constructor]") +{ + type p_type = type::get("constructor_test"); + REQUIRE(p_type.is_valid() == true); + + constructor default_ctor = p_type.get_constructor(); + REQUIRE(default_ctor == true); + REQUIRE(default_ctor.is_valid() == true); + REQUIRE(default_ctor.get_declaring_type() == type::get<constructor_test>()); + + variant instance = default_ctor.invoke(); + REQUIRE(instance.is_valid() == true); + REQUIRE(instance.is_type<constructor_test*>() == true); + + REQUIRE(default_ctor.get_instanciated_type() == type::get<constructor_test*>()); + REQUIRE(default_ctor.get_parameter_types().empty() == true); + REQUIRE(default_ctor.get_signature() == std::string("constructor_test( )")); + + destructor dtor = p_type.get_destructor(); + REQUIRE(dtor.is_valid() == true); + REQUIRE(dtor == true); + REQUIRE(dtor == dtor); + REQUIRE( !(dtor != dtor) ); + REQUIRE(dtor.get_destructed_type() == type::get<constructor_test*>()); + + dtor.invoke(instance); + REQUIRE(instance.is_valid() == false); + + vector<constructor> ctors = p_type.get_constructors(); + REQUIRE(ctors.size() == 4); + for (const auto& ctor : ctors) + { + REQUIRE(ctor.get_instanciated_type() == type::get<constructor_test*>()); + REQUIRE(ctor.is_valid() == true); + REQUIRE(ctor == true); + // negative test + REQUIRE(ctor.invoke_variadic({"This will not work"}).is_valid() == false); + } + + REQUIRE(ctors[0] == ctors[0]); + REQUIRE(ctors[0] != ctors[1]); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test constructor argument forwarding", "[constructor]") +{ + type p_type = type::get("constructor_test"); + REQUIRE(p_type.is_valid() == true); + + constructor ctor_string = p_type.get_constructor({type::get<std::string>()}); + variant instance = ctor_string.invoke(); + REQUIRE(instance.is_valid() == false); // not correctly invoked, one argument needed + + string test_string("rttr is working"); + instance = ctor_string.invoke(test_string); + REQUIRE(instance.is_type<constructor_test*>() == true); + constructor_test* obj = instance.get_value<constructor_test*>(); + REQUIRE(obj->_text == test_string); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test create/destroy via type", "[constructor]") +{ + type t = type::get("constructor_test"); + variant instance = t.create({2, 4}); + + REQUIRE(instance.is_valid() == true); + REQUIRE(instance.is_type<constructor_test*>() == true); + + t.destroy(instance); + REQUIRE(instance.is_valid() == false); + // negative test + REQUIRE(t.create({2, true}).is_valid() == false); + + //////////////////////////////////////////////// + instance = t.create({string("hello")}); + REQUIRE(instance.is_type<constructor_test*>() == true); + t.destroy(instance); + REQUIRE(instance.is_valid() == false); + + //////////////////////////////////////////////// + instance = t.create({}); + REQUIRE(instance.is_type<constructor_test*>() == true); + t.destroy(instance); + REQUIRE(instance.is_valid() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test constructor signature", "[constructor]") +{ + const auto ctors = type::get("constructor_test").get_constructors(); + REQUIRE(ctors.size() == 4); + + REQUIRE(ctors[0].get_signature() == "constructor_test( )"); + REQUIRE(ctors[1].get_signature() == "constructor_test( int, int )"); + REQUIRE(ctors[2].get_signature() == "constructor_test( std::string const & )"); + REQUIRE(ctors[3].get_signature() == "constructor_test( int, int, int, int, int, int, const int* const )"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test metadata in constructor", "[constructor]") +{ + constructor ctor_string = type::get("constructor_test").get_constructor({type::get<std::string>()}); + variant value = ctor_string.get_metadata("SCRIPTABLE"); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); + // integer metadata + value = ctor_string.get_metadata(TOOL_TIP); + REQUIRE(value.is_type<std::string>() == true); + REQUIRE(value.get_value<std::string>() == "This is a ToolTip"); + + // no metadata + constructor ctor_int = type::get("constructor_test").get_constructor({type::get<int>(), type::get<int>()}); + REQUIRE(ctor_int.is_valid() == true); + REQUIRE(ctor_int.get_metadata("SCRIPTABLE").is_valid() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/test_constructor_reflection.h b/src/test/test_constructor_reflection.h new file mode 100644 index 00000000..53d99d4a --- /dev/null +++ b/src/test/test_constructor_reflection.h @@ -0,0 +1,43 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include <rttr/type> + +struct constructor_test +{ + constructor_test() : _x(0), _y(0), _p1(0.0) {} + constructor_test(int x, int y) : _x(x), _y(y), _p1(0.0) {} + constructor_test(const std::string& text) : _x(0), _y(0), _p1(0.0), _text(text) {} + constructor_test(int p1, int p2, int p3, int p4, int p5, int p6, const int* p7) : _x(p1), _y(p2), _p1(0.0) {} + + int _x; + int _y; + double _p1; + std::string _text; +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(constructor_test) \ No newline at end of file diff --git a/src/test/test_enumeration_reflection.cpp b/src/test/test_enumeration_reflection.cpp new file mode 100644 index 00000000..d7848ac6 --- /dev/null +++ b/src/test/test_enumeration_reflection.cpp @@ -0,0 +1,184 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_enumeration_reflection.h" + +#include <rttr/reflect> + +using namespace rttr; +using namespace std; + +#include <iostream> +#include <memory> +#include <functional> + +#include "catch.hpp" + +enum E_MetaData +{ + SCRIPTABLE = 0, + TOOL_TIP = 1, + DESCRIPTION = 2 +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_REGISTER +{ + class_<enum_test>() + .constructor<>() + .constructor<enum_test::E_Alignment, enum_test::E_Orientation>() + .enumeration<enum_test::E_Alignment>({ + {"AlignLeft", enum_test::E_Alignment::AlignLeft}, + {"AlignRight", enum_test::E_Alignment::AlignRight}, + {"AlignHCenter", enum_test::E_Alignment::AlignHCenter}, + {"AlignJustify", enum_test::E_Alignment::AlignJustify} + },{metadata(E_MetaData::SCRIPTABLE, true)}) + .enumeration<enum_test::E_Orientation>({ + {"Horizontal", enum_test::E_Orientation::Horizontal}, + {"Vertical", enum_test::E_Orientation::Vertical} + }, {metadata(E_MetaData::SCRIPTABLE, false)}) + .property("alignment", &enum_test::_alignment) + .property("orientation", &enum_test::_orientation); + + enumeration_<E_DayOfWeek>({ + {"Monday", Monday}, + {"Tuesday", Tuesday}, + {"Wednesday", Wednesday}, + {"Thursday", Thursday}, + {"Friday", Friday}, + {"Saturday", Saturday}, + {"Sunday", Sunday} + }, {metadata("Global_Tag", true)}); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test Enumeration", "[enumeration]") +{ + type enum_type = type::get("enum_test"); + REQUIRE(enum_type.is_valid() == true); + + auto ctor_list = enum_type.get_constructors(); + REQUIRE(ctor_list.size() == 2); + REQUIRE(ctor_list[1].get_parameter_types().size() == 2); + + auto params = ctor_list[1].get_parameter_types(); + REQUIRE(params.size() == 2); + REQUIRE(params[0].is_enumeration() == true); + REQUIRE(params[1].is_enumeration() == true); + + enumeration enum_info_align = params[0].get_enumeration(); + REQUIRE(enum_info_align.is_valid() == true); + REQUIRE(enum_info_align == true); + REQUIRE(enum_info_align.get_declaring_type() == type::get<enum_test>()); + REQUIRE(enum_info_align.get_type() == params[0]); + + enumeration enum_info_orient = params[1].get_enumeration(); + REQUIRE(enum_info_orient.is_valid() == true); + + // create instance from enum value + variant enum_inst = enum_type.create({enum_info_align.key_to_value("AlignHCenter"), enum_info_orient.key_to_value("Vertical")}); + REQUIRE(enum_inst.is_valid() == true); + + property prop = enum_type.get_property("orientation"); + REQUIRE(prop.is_enumeration() == true ); + REQUIRE(prop.get_enumeration() == enum_info_orient ); + REQUIRE(! (prop.get_enumeration() != enum_info_orient) ); + + variant orient_value = enum_type.get_property("orientation").get_value(enum_inst); + variant align_value = enum_type.get_property("alignment").get_value(enum_inst); + REQUIRE(orient_value.is_valid() == true); + REQUIRE(align_value.is_valid() == true); + + REQUIRE(align_value.get_type() == enum_info_align.get_type()); + REQUIRE(orient_value.get_type() == enum_info_orient.get_type()); + + // the underlying type is compiler specific implemented(can be int or unsigned int) + // so simple check if one is returned + REQUIRE(enum_info_align.get_underlying_type().is_valid() == true); + + REQUIRE(align_value.get_value<enum_test::E_Alignment>() == enum_test::AlignHCenter); + REQUIRE(orient_value.get_value<enum_test::E_Orientation>() == enum_test::E_Orientation::Vertical); + + variant bad_value = enum_info_align.key_to_value("WrongEnum"); + std::string bad_key = enum_info_align.value_to_key(5000); + REQUIRE(bad_value.is_valid() == false); + REQUIRE(bad_key == string()); + + std::vector<std::string> enum_keys = enum_info_align.get_keys(); + REQUIRE(enum_keys.size() == 4); + + std::vector<variant> enum_values = enum_info_align.get_values(); + REQUIRE(enum_values.size() == 4); + + const auto underlying_value = static_cast<std::underlying_type<enum_test::E_Alignment>::type>(2); + std::string ret = enum_info_align.value_to_key(underlying_value); + REQUIRE(ret == "AlignRight"); + + const auto bad_underlying_value = static_cast<std::underlying_type<enum_test::E_Alignment>::type>(5); + ret = enum_info_align.value_to_key(bad_underlying_value); // test bad value + REQUIRE(ret == ""); + + enum_type.destroy(enum_inst); + REQUIRE(enum_inst.is_valid() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test global enum", "[enumeration]") +{ + type enum_type = type::get("E_DayOfWeek"); + REQUIRE(enum_type.is_valid() == true); + REQUIRE(enum_type.is_enumeration() == true); + + enumeration e_day = enum_type.get_enumeration(); + REQUIRE(e_day.get_declaring_type().is_valid() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test enumeration metadata", "[enumeration]") +{ + enumeration enum_align = type::get("enum_test::E_Alignment").get_enumeration(); + + variant value = enum_align.get_metadata(E_MetaData::SCRIPTABLE); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); + + // not scriptable + enumeration enum_orient = type::get("enum_test::E_Orientation").get_enumeration(); + value = enum_orient.get_metadata(E_MetaData::SCRIPTABLE); + REQUIRE(value.is_valid() == true); + REQUIRE(value.get_value<bool>() == false); + + // integer metadata + enumeration enum_day = type::get("E_DayOfWeek").get_enumeration(); + value = enum_day.get_metadata("Global_Tag"); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); +} diff --git a/src/test/test_enumeration_reflection.h b/src/test/test_enumeration_reflection.h new file mode 100644 index 00000000..03471f73 --- /dev/null +++ b/src/test/test_enumeration_reflection.h @@ -0,0 +1,72 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include <rttr/type> + + +struct enum_test +{ + enum E_Alignment + { + AlignLeft = 0x0001, + AlignRight = 0x0002, + AlignHCenter = 0x0004, + AlignJustify = 0x0008 + }; + + enum class E_Orientation + { + Horizontal = 0, + Vertical = 1 + }; + + enum_test() : _alignment(E_Alignment::AlignLeft), _orientation(E_Orientation::Vertical) + {} + + enum_test(E_Alignment align, E_Orientation orient) : _alignment(align), _orientation(orient) + {} + + + E_Alignment _alignment; + E_Orientation _orientation; +}; + +enum E_DayOfWeek +{ + Monday = 0, + Tuesday = 1, + Wednesday = 2, + Thursday = 3, + Friday = 4, + Saturday = 5, + Sunday = 6 +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(enum_test) +RTTR_DECLARE_TYPE(enum_test::E_Alignment) +RTTR_DECLARE_TYPE(enum_test::E_Orientation) +RTTR_DECLARE_TYPE(E_DayOfWeek) diff --git a/src/test/test_method_reflection.cpp b/src/test/test_method_reflection.cpp new file mode 100644 index 00000000..318742d5 --- /dev/null +++ b/src/test/test_method_reflection.cpp @@ -0,0 +1,397 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_method_reflection.h" + +#include <rttr/reflect> + +using namespace rttr; +using namespace std; + +#include <iostream> +#include <memory> +#include <functional> +#include <cmath> + +#include "catch.hpp" + +enum E_MetaData +{ + SCRIPTABLE = 0, + TOOL_TIP = 1, + DESCRIPTION = 2 +}; + +static bool free_function_called = false; + +void free_function(bool value) +{ + free_function_called = value; +} + +void free_function(int& value) +{ + value = 42; +} + +std::string& get_global_string() +{ + static std::string text = "hello world"; + return text; +} + + +///////////////////////////////////////////////////////////////////////////////////////// + +bool method_test::method_7_called = false; + +RTTR_REGISTER +{ + class_<method_test>() + .constructor<>() + .method("method_1", &method_test::method_1) + .method("method_2", &method_test::method_2) + .method("method_3", &method_test::method_3) + .method("method_4", &method_test::method_4) + .method("method_5", static_cast<int(method_test::*)(double*)>(&method_test::method_5)) + .method("method_5", static_cast<int(method_test::*)(int,double)>(&method_test::method_5)) + .method("method_6", &method_test::method_6) + .method("method_7", &method_test::method_7) + .method("method_8", &method_test::method_8, {metadata(E_MetaData::SCRIPTABLE, true), metadata("TAG", 42)}) + .method("method_9", &method_test::method_9, {metadata(E_MetaData::SCRIPTABLE, false)}) + .method("method_10", std::function<int(double, bool)>([](double, bool)->int{ return 42;})) + .method("method_raw_array", &method_test::method_raw_array) + .method("method_default", &method_test::method_default_arg) + .method("method_6_ret_ptr", &method_test::method_6, return_reference_as_ptr) + .method("method_6_void", &method_test::method_6, discard_return_value) + ; + + class_<method_test_derived>() + .constructor<>() + .method("method_10", &method_test_derived::method_10); + + class_<method_test_right>() + .method("method_12", &method_test_right::method_12); + + class_<method_test_final>() + .method("method_13", &method_test_final::method_13); + + // test free functions + method_("free_function", static_cast<void(*)(bool)>(&free_function)); + method_("free_function", static_cast<void(*)(int&)>(&free_function)); + + method_("get_global_string_ptr", &get_global_string, return_reference_as_ptr); + method_("get_global_string_void", &get_global_string, discard_return_value); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test method", "[method]") +{ + type t_meth = type::get("method_test"); + REQUIRE(t_meth.is_valid() == true); + variant inst = t_meth.create({}); + method_test& obj = *inst.get_value<method_test*>(); + + //////////////////////////////////////////////////////////// + // invoke tests + variant ret = t_meth.get_method("method_1").invoke(inst); + REQUIRE(obj.method_1_called == true); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<void>() == true); + + //////////////////////////////////////// + obj.method_1_called = false; // reset + method meth = t_meth.get_method("method_1"); + meth.invoke_variadic(inst, {}); + REQUIRE(obj.method_1_called == true); + REQUIRE(meth.get_name() == "method_1"); + REQUIRE(meth.get_parameter_types().empty() == true); + + //////////////////////////////////////// + t_meth.get_method("method_2").invoke(inst); + REQUIRE(obj.method_2_called == true); + obj.method_2_called = false; + meth = t_meth.get_method("method_2"); + meth.invoke_variadic(inst, {}); + REQUIRE(obj.method_2_called == true); + + //////////////////////////////////////// + t_meth.get_method("method_3").invoke(inst, 35); + REQUIRE(obj.method_3_called == true); + REQUIRE(obj.method_3_value == 35); + obj.method_3_called = false; + obj.method_3_value = 0; + t_meth.get_method("method_3").invoke_variadic(inst, {42}); + REQUIRE(obj.method_3_called == true); + REQUIRE(obj.method_3_value == 42); + + //////////////////////////////////////// + t_meth.get_method("method_4").invoke(inst, string("test")); + REQUIRE(obj.method_4_called == true); + string ref_for_method4; + t_meth.get_method("method_4").invoke(inst, ref_for_method4); + REQUIRE(ref_for_method4 == "Text Changed"); + + obj.method_4_called = false; + ref_for_method4 = ""; + t_meth.get_method("method_4").invoke_variadic(inst, {ref_for_method4}); + REQUIRE(obj.method_4_called == true); + REQUIRE(ref_for_method4 == "Text Changed"); + + //////////////////////////////////////// + double arg = 0.0; + method m5_overloaded_1 = t_meth.get_method("method_5", {type::get<double*>()}); + ret = m5_overloaded_1.invoke(inst, &arg); + REQUIRE(obj.method_5_called == true); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 42); + REQUIRE(arg == 22.0); + + //////////////////////////////////////// + arg = 0.0; + ret = m5_overloaded_1.invoke_variadic(inst, {&arg}); + REQUIRE(obj.method_5_called == true); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 42); + REQUIRE(arg == 22.0); + + method m5_overloaded_2 = t_meth.get_method("method_5", {type::get<int>(), type::get<double>()}); + REQUIRE(m5_overloaded_1 == true); + REQUIRE(m5_overloaded_2 == true); + REQUIRE(m5_overloaded_1 != m5_overloaded_2); + + //////////////////////////////////////// + method m6 = t_meth.get_method("method_6"); + REQUIRE(m6.get_return_type() == type::get<std::string>()); + + ret = m6.invoke(inst); + REQUIRE(obj.method_6_called == true); + REQUIRE(ret.is_type<string>() == true); + REQUIRE(ret.get_value<string>() == "Hello World"); + + //////////////////////////////////////// + REQUIRE(t_meth.get_method("method_7").is_static() == true); + ret = t_meth.get_method("method_7").invoke(empty_instance(), 34.0); + REQUIRE(obj.method_7_called == true); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 23); + + ret = t_meth.get_method("method_8").invoke(inst); + REQUIRE(obj.method_8_called == true); + + //////////////////////////////////////// + method_test_derived derived_inst; + derived_inst.get_type().get_method("method_8").invoke(derived_inst); + REQUIRE(derived_inst.method_8_derived_called == true); // the derived virtual function was called + REQUIRE(derived_inst.method_8_called == false); // and not the base function + + //////////////////////////////////////// + method m9 = t_meth.get_method("method_9"); + REQUIRE(m9.get_parameter_types().size() == 10); + REQUIRE(m9.get_parameter_types()[4] == type::get<bool>()); + + ret = m9.invoke_variadic(inst, {1,2,3,4,true,6,7,8,9,10}); + REQUIRE(obj.method_9_called == true); + + + //////////////////////////////////////// + t_meth.get_method("method_default").invoke(derived_inst,3); + REQUIRE(derived_inst.method_default_arg_called == true); + + //////////////////////////////////////////////////////////// + // check up_cast, cross cast and middle in the hierarchy cast through invoke + method_test_final final_obj; + type t_final = type::get(final_obj); + REQUIRE(t_final.get_methods().size() == 18); // +1 overloaded + // test the up cast + t_final.get_method("method_3").invoke(final_obj, 1000); + REQUIRE(final_obj.method_3_called == true); + REQUIRE(final_obj.method_3_value == 1000); + REQUIRE(t_final.get_method("method_3").get_declaring_type() == type::get<method_test>()); + + method_test& up_cast_test = final_obj; + // test the cross cast + t_final.get_method("method_12").invoke(up_cast_test); + REQUIRE(final_obj.method_12_right_called == true); + + // test the middle cast + t_final.get_method("method_10").invoke(up_cast_test, 45); + REQUIRE(final_obj.method_10_derived_called == true); + + //////////////////////////////////////////////////////////// + // test compare operator + REQUIRE(t_meth.get_method("method_default") == t_meth.get_method("method_default")); + REQUIRE(t_meth.get_method("method_default") != t_meth.get_method("method_4")); + + //////////////////////////////////////////////////////////// + // clean up + t_meth.destroy(inst); + REQUIRE(inst.is_valid() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("global methods", "[method]") +{ + free_function_called = false; + method global_meth_1 = type::get_global_method("free_function", {type::get<bool>()}); + REQUIRE(global_meth_1 == true); + REQUIRE(global_meth_1.get_parameter_types()[0] == type::get<bool>()); + variant success = global_meth_1.invoke(empty_instance()); + REQUIRE(success.is_valid() == false); + REQUIRE(free_function_called == false); + + success = global_meth_1.invoke(empty_instance(), true); + REQUIRE(success.is_valid() == true); + REQUIRE(free_function_called == true); + + method global_meth_2 = type::get_global_method("free_function", {type::get<int>()}); + REQUIRE(global_meth_2 == true); + int arg = 0; + global_meth_2.invoke(empty_instance(), arg); + REQUIRE(arg == 42); + + REQUIRE(global_meth_1 != global_meth_2); + + auto list = type::get_global_methods(); + REQUIRE(list.size() >= 2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("ShortCut via type - method invoke", "[method]") +{ + // with instance + method_test_final obj; + variant success = type::get("method_test_final").invoke("method_5", obj, {5, 23.0}); + REQUIRE(success.is_valid() == true); + REQUIRE(obj.method_5_overloaded_called == true); + + double arg = 0.0; + success = type::get("method_test_final").invoke("method_5", obj, {&arg}); + REQUIRE(success.is_valid() == true); + REQUIRE(obj.method_5_called == true); + + + // global method + free_function_called = false; + success = type::invoke("free_function", {true}); + REQUIRE(success.is_valid() == true); + REQUIRE(free_function_called == true); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test method arrays", "[method]") +{ + method_test obj; + method meth_array = type::get(obj).get_method("method_raw_array"); + REQUIRE(meth_array.is_valid() == true); + + int raw_int_9[9]; + variant ret = meth_array.invoke(obj, raw_int_9); + REQUIRE(obj.method_raw_array_called == false); + REQUIRE(ret.is_valid() == false); + + int raw_int[10]; + ret = meth_array.invoke(obj, raw_int); + REQUIRE(obj.method_raw_array_called == true); + REQUIRE(ret.is_valid() == true); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test method signature", "[method]") +{ + const auto methods = type::get("method_test_final").get_methods(); + REQUIRE(methods.size() == 18); + + REQUIRE(methods[0].get_signature() == "method_13( )"); + REQUIRE(methods[5].get_signature() == "method_4( std::string & )"); + REQUIRE(methods[6].get_signature() == "method_5( double* )"); + REQUIRE(methods[7].get_signature() == "method_5( int, double )"); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("method policies", "[method]") +{ + method_test obj; + type meth_type = type::get(obj); + method m6_ptr = meth_type.get_method("method_6_ret_ptr"); + REQUIRE(m6_ptr.get_return_type() == type::get<const std::string*>()); + variant ret = m6_ptr.invoke(obj); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<const std::string*>() == true); + + method m6_void = meth_type.get_method("method_6_void"); + REQUIRE(m6_void.get_return_type() == type::get<void>()); + ret = m6_void.invoke(obj); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<void>() == true); + + // global functions + method meth_g_ptr = type::get_global_method("get_global_string_ptr"); + REQUIRE(meth_g_ptr.get_return_type() == type::get<std::string*>()); + ret = meth_g_ptr.invoke(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<std::string*>() == true); + + method meth_g_void = type::get_global_method("get_global_string_void"); + REQUIRE(meth_g_void.get_return_type() == type::get<void>()); + ret = meth_g_void.invoke(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<void>() == true); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test method metadata", "[method]") +{ + method m8 = type::get("method_test_final").get_method("method_8"); + variant value = m8.get_metadata(E_MetaData::SCRIPTABLE); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); + // string metadata + value = m8.get_metadata("TAG"); + REQUIRE(value.is_valid() == true); + REQUIRE(value.get_value<int>() == 42); + + // no metadata + method m7 = type::get("method_test_final").get_method("method_7"); + REQUIRE(m7.is_valid() == true); + REQUIRE(m7.get_metadata(E_MetaData::SCRIPTABLE).is_valid() == false); + + // not scriptable + method m9 = type::get("method_test_final").get_method("method_9"); + value = m9.get_metadata(E_MetaData::SCRIPTABLE); + REQUIRE(value.is_valid() == true); + REQUIRE(value.get_value<bool>() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/test_method_reflection.h b/src/test/test_method_reflection.h new file mode 100644 index 00000000..180db9dd --- /dev/null +++ b/src/test/test_method_reflection.h @@ -0,0 +1,106 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include <rttr/type> +#include <string> + +struct method_test +{ + virtual ~method_test() {} + void method_1() { method_1_called = true; } // basic simple method + void method_2() const volatile { method_2_called = true; } // some more qualifiers + void method_3(int value) { method_3_called = true; method_3_value = value;} // call method with one parameter + void method_4(std::string& text) { text = "Text Changed"; method_4_called = true; } // call method with parameter by reference + int method_5(double* arg) { method_5_called = true; *arg = 22.0; return 42; }// method with return value + const std::string& method_6() const { method_6_called = true; return dummy_text; } // method which return a value by reference + static int method_7(double value){ method_7_called = true; return 23;} // static method + virtual void method_8() { method_8_called = true; } // virtual method + void method_9(int, int, int, int, bool, int, int, int, int, int) { method_9_called = true; } // method with many arguments + void method_default_arg(int var = 23) { method_default_arg_called = true; } // method with default argument + void method_raw_array(int (&arr)[10]) { method_raw_array_called = true; } + + + int method_5(int, double) { method_5_overloaded_called = true; return 42; } // overloaded method + + double dummy_data = 12; + std::string dummy_text = "Hello World"; + int method_3_value = 0; + + bool method_1_called = false; + mutable bool method_2_called = false; + bool method_3_called = false; + bool method_4_called = false; + bool method_5_called = false; + mutable bool method_6_called = false; + static bool method_7_called; + bool method_8_called = false; + bool method_9_called = false; + bool method_5_overloaded_called = false; + bool method_default_arg_called = false; + bool method_raw_array_called = false; + + RTTR_ENABLE() +}; + +RTTR_DECLARE_TYPE(int[10]) +RTTR_DECLARE_TYPE(int[9]) + +struct method_test_derived : method_test +{ + + virtual void method_8() { method_8_derived_called = true; } + void method_10(int value) { method_10_derived_called = true;} // here we want to check if a base ptr can be converted to the middle + + bool method_8_derived_called = false; + bool method_10_derived_called = false; + + RTTR_ENABLE(method_test) +}; + +struct method_test_right +{ + virtual ~method_test_right() {} + void method_12() { method_12_right_called = true;} + + bool method_12_right_called = false; + RTTR_ENABLE() +}; + +struct method_test_final : method_test_derived, method_test_right +{ + + void method_13() { method_13_final_called = true;} + + bool method_13_final_called = false; + + RTTR_ENABLE(method_test_derived, method_test_right) +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(method_test) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(method_test_derived) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(method_test_right) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(method_test_final) diff --git a/src/test/test_property_reflection.cpp b/src/test/test_property_reflection.cpp new file mode 100644 index 00000000..324d73a3 --- /dev/null +++ b/src/test/test_property_reflection.cpp @@ -0,0 +1,627 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_property_reflection.h" + +#include <rttr/reflect> + +using namespace rttr; +using namespace std; + +#include <iostream> +#include <memory> +#include <functional> + +#include "catch.hpp" + +///////////////////////////////////////////////////////////////////////////////////////// +// init static variables and some global functions added as properties to the test class + +int get_propert_p9() { return 42; } +int property_test::_p5= 23; +const int property_test::_p6 = 12; +static float singleton_property = 3.1425f; +static std::string p11; + +const std::string& my_p11_getter() +{ + return p11; +} + +void my_p11_setter(const std::string& text) +{ + p11 = text; +} + +enum E_MetaData +{ + SCRIPTABLE = 0, + TOOL_TIP = 1, + DESCRIPTION = 2 +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +static const double pi = 3.124256; +static std::string Global_Text; + +std::string& get_global_text() { return Global_Text; } +static std::vector<int> global_array(1000, 42); +const int* ptr_type; +///////////////////////////////////////////////////////////////////////////////////////// + +RTTR_REGISTER +{ + using namespace rttr; + class_<property_test>() + .constructor<>() + .property("p1", &property_test::_p1) + .property_readonly("p2", &property_test::_p2) + .property("p3", &property_test::_p3) + .property_readonly("p4", &property_test::_p4) + .property("p5", &property_test::_p5, {metadata(E_MetaData::SCRIPTABLE, true), metadata(E_MetaData::TOOL_TIP, string("This is property 5."))}) + .property_readonly("p6", &property_test::_p6, {metadata(E_MetaData::SCRIPTABLE, false)}) + .property("p7", &property_test::get_p7, &property_test::set_p7) + .property("p8", &singleton_property, {metadata("Global_Tag", true)}) + .property_readonly("p9", &get_propert_p9) + .property_readonly("p10", std::function<int()>([](){ return 45;})) + .property("p11", &my_p11_getter, &my_p11_setter) + .property("p12", std::function<const string&(void)>(&my_p11_getter), std::function<void(const std::string&)>(&my_p11_setter)) + .property("array_copy", &property_test::_array) + .property("array", &property_test::_array, bind_property_as_ptr) + .property("raw_array", &property_test::_other_array, bind_property_as_ptr) + .property_readonly("p7_as_ptr_read_only", &property_test::get_p7, bind_property_as_ptr) + .property("p7_as_ptr", &property_test::get_p7, &property_test::set_p7, bind_property_as_ptr) + ; + + ///////////////////////////////////////// + + class_<ns_property::top>() + .property("p1", &ns_property::top::_p1); + + class_<ns_property::left>() + .constructor<>() + .property("p2", &ns_property::left::_p2); + + class_<ns_property::right>() + .constructor<>() + .property("p3", &ns_property::right::_p3) + .property("p2", &p11); // double property + + class_<ns_property::right_2>() + .constructor<>() + .property("p4", &ns_property::right_2::_p4); + + class_<ns_property::bottom>() + .constructor<>() + .property("p5", &ns_property::bottom::_p5); + ///////////////////////////////////////// + + property_readonly_("PI", &pi); + property_("Global_Text", &Global_Text); + + // test policies + property_("p1_as_ptr", &global_array, bind_property_as_ptr); + property_readonly_("p2_as_ptr", &get_global_text, bind_property_as_ptr); + property_readonly_("p3_as_ptr", std::function<const string&(void)>(&my_p11_getter), bind_property_as_ptr); + property_("p4_as_ptr", std::function<const string&(void)>(&my_p11_getter), std::function<void(const std::string&)>(&my_p11_setter), bind_property_as_ptr); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test Properties", "[property]") +{ + type prop_type = type::get("property_test"); + REQUIRE(prop_type.is_valid() == true); + + REQUIRE(prop_type.get_property("p1").is_valid() == true); + REQUIRE(prop_type.get_property("p2").is_valid() == true); + REQUIRE(prop_type.get_property("p3").is_valid() == true); + REQUIRE(prop_type.get_property("p4").is_valid() == true); + REQUIRE(prop_type.get_property("p5").is_valid() == true); + REQUIRE(prop_type.get_property("p6").is_valid() == true); + REQUIRE(prop_type.get_property("p7").is_valid() == true); + // invalid test + REQUIRE(prop_type.get_property("p23").is_valid() == false); + + SECTION("Test properties - read/write") + { + variant prop_inst = prop_type.create({}); + REQUIRE(prop_type.is_valid() == true); + + property p1 = prop_type.get_property("p1"); + REQUIRE(p1.is_readonly() == false); + + // access + bool ret = p1.set_value(prop_inst, 2); + REQUIRE(ret == true); + + variant val = p1.get_value(prop_inst); + REQUIRE(val.is_type<int>() == true); + + REQUIRE(val.get_value<int>() == 2); + + /////////////////////////////////////////// + // class bool property + property p3 = prop_type.get_property("p3"); + REQUIRE(p3.is_readonly() == false); + + // access + ret = p3.set_value(prop_inst, false); + REQUIRE(ret == true); + + val = p3.get_value(prop_inst); + REQUIRE(val.is_type<bool>() == true); + REQUIRE(val.get_value<bool>() == false); + + /////////////////////////////////////////// + // class static int property + property p5 = prop_type.get_property("p5"); + REQUIRE(p5.is_readonly() == false); + + // access + ret = p5.set_value(prop_inst, 1000); + REQUIRE(ret == true); + + val = p5.get_value(prop_inst); + REQUIRE(val.is_type<int>() == true); + REQUIRE(val.get_value<int>() == 1000); + + + /////////////////////////////////////////// + // a global static property + property p8 = prop_type.get_property("p8"); + REQUIRE(p8.is_readonly() == false); + REQUIRE(p8.is_static() == true); + + // access + ret = p8.set_value(prop_inst, 5.678f); + REQUIRE(ret == true); + + val = p8.get_value(prop_inst); + REQUIRE(val.is_type<float>() == true); + REQUIRE(val.get_value<float>() == 5.678f); + + /////////////////////////////////////////// + // a getter/setter member function property + property p7 = prop_type.get_property("p7"); + REQUIRE(p7.is_readonly() == false); + + // access + ret = p7.set_value(prop_inst, string("hello world")); + REQUIRE(ret == true); + + val = p7.get_value(prop_inst); + REQUIRE(val.is_type<std::string>() == true); + REQUIRE(val.get_value<std::string>() == "hello world"); + + /////////////////////////////////////////// + // a getter/setter function property + property p11 = prop_type.get_property("p11"); + REQUIRE(p11.is_readonly() == false); + + // access + ret = p11.set_value(prop_inst, string("hello world")); + REQUIRE(ret == true); + + val = p11.get_value(prop_inst); + REQUIRE(val.is_type<std::string>() == true); + REQUIRE(val.get_value<std::string>() == "hello world"); + + /////////////////////////////////////////// + // a getter/setter function property + property p12 = prop_type.get_property("p12"); + REQUIRE(p12.is_readonly() == false); + + // access + ret = p12.set_value(prop_inst, string("Another Text")); + REQUIRE(ret == true); + + val = p12.get_value(prop_inst); + REQUIRE(val.is_type<std::string>() == true); + REQUIRE(val.get_value<std::string>() == "Another Text"); + + // test property on stack + variant prop_stack = property_test(); + p7.set_value(prop_stack, string("Hello World")); + + val = p7.get_value(prop_stack); + REQUIRE(val.is_type<std::string>() == true); + REQUIRE(val.get_value<std::string>() == "Hello World"); + + prop_type.destroy(prop_inst); + REQUIRE(prop_inst.is_valid() == false); + } + + SECTION("test read only properties") + { + variant prop_inst = prop_type.create({}); + REQUIRE(prop_type.is_valid() == true); + + // self reflected const property + property p2 = prop_type.get_property("p2"); + bool ret = p2.set_value(prop_inst, 45); + REQUIRE(ret == false); + REQUIRE(p2.is_readonly() == true); + + variant val = p2.get_value(prop_inst); + REQUIRE(val.is_valid() == true); + REQUIRE(val.is_type<short int>() == true); + REQUIRE(val.get_value<short int>() == 12); + + /////////////////////////////////////////// + // class const property + property p4 = prop_type.get_property("p4"); + REQUIRE(p4.get_declaring_type() == prop_type); + REQUIRE(p4.get_type() == type::get<double>()); + + ret = p4.set_value(prop_inst, 12.0); + REQUIRE(ret == false); + REQUIRE(p4.is_readonly() == true); + + val = p4.get_value(prop_inst); + REQUIRE(val.is_valid() == true); + REQUIRE(val.is_type<double>() == true); + REQUIRE(val.get_value<double>() == 23.0); + + /////////////////////////////////////////// + // class static const property + property p6 = prop_type.get_property("p6"); + ret = p6.set_value(prop_inst, 12.0); + REQUIRE(ret == false); + REQUIRE(p6.is_readonly() == true); + + val = p6.get_value(prop_inst); + REQUIRE(val.is_valid() == true); + REQUIRE(val.is_type<int>() == true); + REQUIRE(val.get_value<int>() == 12); + + /////////////////////////////////////////// + // global function property + property p9 = prop_type.get_property("p9"); + ret = p9.set_value(prop_inst, 12.0); + REQUIRE(ret == false); + REQUIRE(p9.is_readonly() == true); + + val = p9.get_value(prop_inst); + REQUIRE(val.is_valid() == true); + REQUIRE(val.is_type<int>() == true); + REQUIRE(val.get_value<int>() == 42); + + /////////////////////////////////////////// + // std::function property + property p10 = prop_type.get_property("p10"); + ret = p10.set_value(prop_inst, 12.0); + REQUIRE(ret == false); + REQUIRE(p10.is_readonly() == true); + + val = p10.get_value(prop_inst); + REQUIRE(val.is_valid() == true); + REQUIRE(val.is_type<int>() == true); + REQUIRE(val.get_value<int>() == 45); + + prop_type.destroy(prop_inst); + REQUIRE(prop_inst.is_valid() == false); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test property array access", "[property]") +{ + + SECTION("test raw array") + { + property_test obj; + type bottom_type = type::get(obj); + property prop = bottom_type.get_property("array_copy"); + REQUIRE(prop == true); + variant var = prop.get_value(obj); + REQUIRE(var.is_type<std::vector<int>>() == true); + REQUIRE(var.can_convert<variant_array>()== true); + + variant_array var_array = var.to_array(); + var_array.set_size(10); + prop.set_value(obj, var_array); + REQUIRE(obj._array.size() == 10); + } + + +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test property policy", "[property]") +{ + SECTION("global property - array - return value converted to pointer") + { + property p1 = type::get_global_property("p1_as_ptr"); + REQUIRE(p1.get_type() == type::get<std::vector<int>*>()); + REQUIRE(p1.is_readonly() == false); + REQUIRE(p1.is_array() == true); + + variant ret = p1.get_value(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<std::vector<int>*>() == true); + bool could_set_value = p1.set_value(empty_instance(), ret); + REQUIRE(could_set_value == true); + + std::vector<int>* vec = ret.get_value<std::vector<int>*>(); + REQUIRE(vec == &global_array); + } + + SECTION("global property - read only function - return value converted to pointer") + { + property p2 = type::get_global_property("p2_as_ptr"); + REQUIRE(p2.get_type() == type::get<const std::string*>()); + REQUIRE(p2.is_readonly() == true); + + variant ret = p2.get_value(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<const std::string*>() == true); + p2.set_value(empty_instance(), ret); + } + + SECTION("global property - read only std::function - return value converted to pointer") + { + property p3 = type::get_global_property("p3_as_ptr"); + REQUIRE(p3.get_type() == type::get<const std::string*>()); + REQUIRE(p3.is_readonly() == true); + + variant ret = p3.get_value(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<const std::string*>() == true); + } + + + SECTION("global property - read only std::function - return value converted to pointer") + { + property p3 = type::get_global_property("p3_as_ptr"); + REQUIRE(p3.get_type() == type::get<const std::string*>()); + REQUIRE(p3.is_readonly() == true); + + variant ret = p3.get_value(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<const std::string*>() == true); + } + + SECTION("global property - setter/getter function - return value converted to pointer") + { + property p4 = type::get_global_property("p4_as_ptr"); + REQUIRE(p4.is_array() == false); + REQUIRE(p4.get_type() == type::get<const std::string*>()); + REQUIRE(p4.is_readonly() == false); + + variant ret = p4.get_value(empty_instance()); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.is_type<const std::string*>() == true); + + bool could_set_value = p4.set_value(empty_instance(), ret); + REQUIRE(could_set_value == true); + } + + SECTION("class property - raw array") + { + property_test obj; + type prop_type = type::get(obj); + + + property array_prop = prop_type.get_property("raw_array"); + REQUIRE(array_prop.is_array() == true); + variant array_obj = array_prop.get_value(obj); // the array is returned by pointer + variant_array other_array = array_obj.to_array(); + REQUIRE(other_array.is_valid() == true); + + variant ret = other_array.get_value(23); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 23); + + for (std::size_t i = 0; i < other_array.get_size(); ++i) + other_array.set_value(i, static_cast<int>(i * 2)); + + // did we really set the value? + for (std::size_t i = 0; i < other_array.get_size(); ++i) + REQUIRE(obj._other_array[i] == i * 2); + + auto foo = other_array.get_type().get_name(); + bool wasSet = array_prop.set_value(obj, other_array); + REQUIRE(wasSet == true); + } + + SECTION("class property - array") + { + property_test obj; + type prop_type = type::get(obj); + property array_prop = prop_type.get_property("array"); + REQUIRE(array_prop.is_array() == true); + variant array_obj = array_prop.get_value(obj); // the array is returned by pointer + variant_array vec_array = array_obj.to_array(); + REQUIRE(vec_array.is_valid() == true); + REQUIRE(array_obj.is_type<std::vector<int>*>() == true); + REQUIRE(array_obj.get_value<std::vector<int>*>() == &obj._array); + } + + SECTION("class property - getter member function") + { + property_test obj; + type prop_type = type::get(obj); + property p7_as_ptr = prop_type.get_property("p7_as_ptr_read_only"); + REQUIRE(p7_as_ptr == true); + REQUIRE(p7_as_ptr.is_readonly() == true); + REQUIRE(p7_as_ptr.get_type() == type::get<const std::string*>()); + } + + SECTION("class property - getter/setter member function") + { + property_test obj; + type prop_type = type::get(obj); + property p7_as_ptr = prop_type.get_property("p7_as_ptr"); + REQUIRE(p7_as_ptr == true); + REQUIRE(p7_as_ptr.is_readonly() == false); + REQUIRE(p7_as_ptr.get_type() == type::get<const std::string*>()); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test property inheritance", "[property]") +{ + type t = type::get("ns_property::bottom"); + auto props = t.get_properties(); + REQUIRE(props.size() == 6); + + REQUIRE(props[0].get_name() == "p5"); // bottom + REQUIRE(props[1].get_name() == "p2"); // left + REQUIRE(props[2].get_name() == "p3"); // right + REQUIRE(props[3].get_name() == "p2"); // right + REQUIRE(props[4].get_name() == "p1"); // top + REQUIRE(props[5].get_name() == "p4"); // right + + REQUIRE(props[5] == props[5]); + REQUIRE(props[5] != props[1]); + + ns_property::bottom instance; + ns_property::top* top = &instance; + // try access from top instance a property in the most derived class (bottom) + variant ret = props[0].get_value(top); + REQUIRE(ret.is_type<double>() == true); + REQUIRE(ret.get_value<double>() == 23.0); + // try to change the value + props[0].set_value(top, 42.0); + REQUIRE(instance._p5 == 42.0); + + // and now the other way around, from bottom a top property + ret = props[4].get_value(&instance); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 12); + // try to change the value + props[4].set_value(top, 2000); + REQUIRE(instance._p1 == 2000); + + // check double declared property is from left class + REQUIRE(props[1].get_declaring_type() == type::get("ns_property::left")); + // the right class has still its property? + REQUIRE(type::get("ns_property::right").get_property("p2").is_valid() == true); + + property p1 = type::get("ns_property::bottom").get_property("p1"); + REQUIRE(p1 == true); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test get/set global property", "[property]") +{ + property pi_prop = type::get_global_property("PI"); + REQUIRE(pi_prop == true); + REQUIRE(pi_prop.set_value(empty_instance(), 3.2) == false); + + property global_text = type::get_global_property("Global_Text"); + REQUIRE(global_text == true); + REQUIRE(global_text.set_value(empty_instance(), std::string("Hello World")) == true); + REQUIRE(global_text.get_value(empty_instance()).get_value<std::string>() == "Hello World"); + + auto list = type::get_global_properties(); + REQUIRE(list.size() >= 2); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test property shortcuts to set/get property", "[property]") +{ + SECTION("test set property with instance") + { + property_test obj; + variant ret = type::get(obj).get_property_value("p2", obj); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.get_value<short int>() == 12); + + bool success = type::get(obj).set_property_value("p1", obj, 500); + REQUIRE(success == true); + REQUIRE(obj._p1 == 500); + } + + SECTION("test set property with derived instance") + { + // derived obj + ns_property::bottom bottom; + ns_property::right_2& obj = bottom; + + variant ret = type::get(bottom).get_property_value("p5", obj); + REQUIRE(ret.is_valid() == true); + REQUIRE(ret.get_value<double>() == 23.0); + + bool success = type::get(bottom).set_property_value("p5", obj, 500.0); + REQUIRE(success == true); + REQUIRE(bottom._p5 == 500.0); + } + + SECTION("test set global property") + { + bool success = type::set_property_value("Global_Text", string("Another Text")); + REQUIRE(success == true); + + variant ret = type::get_property_value("Global_Text"); + REQUIRE(ret.get_value<std::string>() == string("Another Text")); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("Test property metadata", "[property]") +{ + property p5 = type::get("property_test").get_property("p5"); + variant value = p5.get_metadata(E_MetaData::SCRIPTABLE); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); + + value = p5.get_metadata(E_MetaData::TOOL_TIP); + REQUIRE(value.is_type<std::string>() == true); + REQUIRE(value.get_value<std::string>() == "This is property 5."); + + // no metadata + property p4 = type::get("property_test").get_property("p4"); + REQUIRE(p4.is_valid() == true); + REQUIRE(p4.get_metadata(E_MetaData::SCRIPTABLE).is_valid() == false); + + // not scriptable property + property p6 = type::get("property_test").get_property("p6"); + REQUIRE(p6.is_valid() == true); + REQUIRE(p6.get_metadata(E_MetaData::SCRIPTABLE).is_valid() == true); + REQUIRE(p6.get_metadata(E_MetaData::SCRIPTABLE).get_value<bool>() == false); + + + // string metdadata + property p8 = type::get("property_test").get_property("p8"); + REQUIRE(p8 == true); + + value = p8.get_metadata("Global_Tag"); + REQUIRE(value.is_type<bool>() == true); + REQUIRE(value.get_value<bool>() == true); + +} + +///////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/test_property_reflection.h b/src/test/test_property_reflection.h new file mode 100644 index 00000000..017ac97a --- /dev/null +++ b/src/test/test_property_reflection.h @@ -0,0 +1,126 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include <rttr/type> + +struct property_test +{ + property_test() : _p1(0), _p2(12), _p3(true), _p4(23) + { + _array.resize(1000); + for (int i = 0; i < 100; ++i) + _other_array[i] = i; + } + virtual ~property_test() {} + + const std::string& get_p7() const { return _p7; } + void set_p7(const std::string& text) { _p7 = text; } + int get_prop() const volatile { return 22; } + + int _p1; + short int _p2; + bool _p3; + const double _p4; + static int _p5; + static const int _p6; + std::string _p7; + double* _p8; + std::vector<int> _array; + int _other_array[100]; + + RTTR_REGISTER_FRIEND; +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(property_test) +RTTR_DECLARE_TYPE(int[100]) +RTTR_DECLARE_TYPE(int(*)[100]) +RTTR_DECLARE_TYPE(const int(*)[100]) + + +///////////////////////////////////////////////////////////////////////////////////////// +// test derived properties + +namespace ns_property +{ +struct top +{ + virtual ~top() {} + top() : _p1(12){} + int _p1; + RTTR_ENABLE() +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct left : virtual top +{ + + left() : _p2(true){} + bool _p2; + + RTTR_ENABLE(top) +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct right : virtual top +{ + + right() : _p3(true){} + bool _p3; + + RTTR_ENABLE(top) +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct right_2 +{ + virtual ~right_2() {} + right_2() : _p4(true){} + bool _p4; + RTTR_ENABLE() +}; + +///////////////////////////////////////////////////////////////////////////////////////// + +struct bottom : left, right, right_2 +{ + bottom() : _p5(23.0){} + double _p5; + RTTR_ENABLE(left, right, right_2) +}; + +} + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(ns_property::top) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(ns_property::left) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(ns_property::right) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(ns_property::right_2) +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(ns_property::bottom) + +///////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/test_type.cpp b/src/test/test_type.cpp new file mode 100644 index 00000000..25bd6a2a --- /dev/null +++ b/src/test/test_type.cpp @@ -0,0 +1,355 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "test_classes.h" +#include "catch.hpp" + +#include <rttr/type> + +#include <vector> +#include <map> +#include <string> + +using namespace rttr; + +typedef std::map<int, std::string> IntToStringMap; +typedef std::map<int, int> IntToIntMap; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(IntToStringMap) + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(IntToIntMap) + +RTTR_DECLARE_TYPE(int***) + +RTTR_DECLARE_TYPE(int[10]) + +enum E_Alignment +{ + LEFT, + RIGHT, + BOTTOM, + DOWN +}; + +RTTR_DECLARE_TYPE(E_Alignment) + +TEST_CASE("TypeInfoTests - BasicTests", "[type]") +{ + using namespace rttr; + + SECTION("simple basic check") + { + int intVar = 23; + const type intTypeInfo = type::get(intVar); + CHECK(intTypeInfo.get_name() == "int"); + CHECK(intTypeInfo == type::get<int>()); + + bool boolVar = true; + const type boolTypeInfo = type::get(boolVar); + CHECK(boolTypeInfo.get_name() == "bool"); + CHECK(boolTypeInfo == type::get<bool>()); + + CHECK(boolTypeInfo != intTypeInfo); + + int ***intPtr = NULL; + CHECK(type::get<int***>() == type::get(intPtr)); + + } + + SECTION("check pointer types") + { + int intVar = 23; + int* intPtrVar = &intVar; + const type intPtrTypeInfo = type::get(intPtrVar); + CHECK(intPtrTypeInfo.get_name() == "int*"); + CHECK(intPtrTypeInfo == type::get<int*>()); + + bool boolVar = true; + bool* boolPtrVar = &boolVar; + const type boolPtrTypeInfo = type::get(boolPtrVar); + CHECK(boolPtrTypeInfo.get_name() == "bool*"); + CHECK(boolPtrTypeInfo == type::get<bool*>()); + + CHECK(boolPtrTypeInfo != intPtrTypeInfo); + } + + SECTION("check that cv was removed") + { + int intVar = 42; + const int constIntVar = 42; + CHECK(type::get(intVar) == type::get(constIntVar)); + CHECK(type::get<int>() == type::get(constIntVar)); + CHECK(type::get<int>() == type::get<const int>()); + CHECK(type::get<int>() == type::get<const int &>()); + + CHECK(type::get<int*>() == type::get(&intVar)); + CHECK(type::get<int*>() == type::get<int *const>()); + CHECK(type::get<const int*>() == type::get(&constIntVar)); + CHECK(type::get<const int*>() == type::get<const int *const>()); + + const int& intConstRef = intVar; + CHECK(type::get<int>() == type::get(intConstRef)); + int*** ptr = NULL; + CHECK(type::get<int***>() == type::get(ptr)); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - ComplexerTypes", "[type]") +{ + std::vector<int> myList; + std::vector<int> myList2; + CHECK(type::get<std::vector<int> >() == type::get(myList)); + CHECK(type::get(myList) == type::get(myList2)); + + IntToStringMap myMap; + std::map<int, std::string> myMap2; + + CHECK((type::get<std::map<int, std::string> >() == type::get<IntToStringMap>())); + CHECK((type::get<std::map<int, std::string> >() == type::get(myMap))); + CHECK((type::get<IntToStringMap>() == type::get(myMap))); + CHECK((type::get(myMap) == type::get(myMap2))); + + CHECK((type::get(myMap) != type::get<std::map<int, int> >())); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - SingleClassInheritance", "[type]") +{ + + { + ClassSingle6A instance6A; + ClassSingleBase& baseSingle = instance6A; + + // down cast cast + CHECK(rttr_cast<ClassSingle6A*>(&baseSingle) != NULL); + CHECK(rttr_cast<ClassSingle3A*>(&baseSingle) != NULL); + CHECK(rttr_cast<ClassSingle6B*>(&baseSingle) == NULL); + + // up cast cast + CHECK(rttr_cast<ClassSingleBase*>(&instance6A) != NULL); + CHECK(rttr_cast<ClassSingle3A*>(&instance6A) != NULL); + CHECK(rttr_cast<ClassSingle1A*>(&instance6A) != NULL); + } + + { + ClassSingle6E instance6E; + ClassSingleBase& baseSingle = instance6E; + + // down cast cast + CHECK(rttr_cast<ClassSingle6A*>(&baseSingle) == NULL); + CHECK(rttr_cast<ClassSingle3E*>(&baseSingle) != NULL); + CHECK(rttr_cast<ClassSingle6E*>(&baseSingle) != NULL); + + // up cast cast + CHECK(rttr_cast<ClassSingleBase*>(&instance6E) != NULL); + CHECK(rttr_cast<ClassSingle3E*>(&instance6E) != NULL); + CHECK(rttr_cast<ClassSingle1E*>(&instance6E) != NULL); + CHECK(rttr_cast<ClassSingle6E*>(&instance6E) != NULL); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - MultipleClassInheritance", "[type]") +{ + { + FinalClass final; + ClassMultipleBaseA& baseMultiA = final; + ClassMultipleBaseB& baseMultiB = final; + ClassMultipleBaseC& baseMultiC = final; + ClassMultipleBaseD& baseMultiD = final; + ClassMultipleBaseE& baseMultiE = final; + + // down cast cast + CHECK(rttr_cast<FinalClass*>(&baseMultiA) != NULL); + CHECK(rttr_cast<FinalClass*>(&baseMultiB) != NULL); + CHECK(rttr_cast<FinalClass*>(&baseMultiC) != NULL); + CHECK(rttr_cast<FinalClass*>(&baseMultiD) != NULL); + CHECK(rttr_cast<FinalClass*>(&baseMultiE) != NULL); + + // up cast cast + CHECK(rttr_cast<ClassMultipleBaseA*>(&final) != NULL); + CHECK(rttr_cast<ClassMultipleBaseB*>(&final) != NULL); + CHECK(rttr_cast<ClassMultipleBaseC*>(&final) != NULL); + CHECK(rttr_cast<ClassMultipleBaseD*>(&final) != NULL); + CHECK(rttr_cast<ClassMultipleBaseE*>(&final) != NULL); + + // down cast cast to the middle + CHECK(rttr_cast<ClassMultiple3A*>(&baseMultiA) != NULL); + CHECK(rttr_cast<ClassMultiple3B*>(&baseMultiB) != NULL); + CHECK(rttr_cast<ClassMultiple3C*>(&baseMultiC) != NULL); + CHECK(rttr_cast<ClassMultiple3D*>(&baseMultiD) != NULL); + CHECK(rttr_cast<ClassMultiple3E*>(&baseMultiE) != NULL); + } + + { + FinalClass final; + ClassMultiple6A classMulti6A; + ClassMultiple3B classMulti3B; + ClassMultiple1E classMulti1E; + + // check for invalid casts + CHECK(rttr_cast<FinalClass*>(&classMulti6A) == NULL); + + CHECK(rttr_cast<ClassMultiple4B*>(&classMulti3B) == NULL); + + CHECK(rttr_cast<ClassMultiple2E*>(&classMulti1E) == NULL); + CHECK(rttr_cast<ClassMultiple6E*>(&classMulti1E) == NULL); + + // check for valid casts + CHECK(rttr_cast<ClassMultiple5A*>(&classMulti6A) != NULL); + CHECK(rttr_cast<ClassMultipleBaseA*>(&classMulti6A) != NULL); + + CHECK(rttr_cast<ClassMultiple2B*>(&classMulti3B) != NULL); + CHECK(rttr_cast<ClassMultiple1B*>(&classMulti3B) != NULL); + + CHECK(rttr_cast<ClassMultipleBaseE*>(&classMulti1E) != NULL); + CHECK(rttr_cast<ClassMultiple1E*>(&classMulti1E) != NULL); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Virtual Inheritance", "[type]") +{ + DiamondBottom diamond; + + DiamondTop* base = ⋄ + DiamondLeft* left = ⋄ + DiamondRight* right = ⋄ + + CHECK(rttr_cast<DiamondBottom*>(base) == &diamond); // top to bottom cast + CHECK(rttr_cast<DiamondTop*>(&diamond) == base); // bottom to top + CHECK(rttr_cast<DiamondLeft*>(base) == left); // base to one level up - left class + CHECK(rttr_cast<DiamondRight*>(base) == right); // base to one level up - right class +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - TypeId/ClassInheritance", "[type]") +{ + ClassSingle6A instance6A; + ClassSingleBase& baseSingle = instance6A; + ClassSingleBase* baseSinglePtr = &instance6A; + + CHECK(type::get<ClassSingleBase*>() == type::get(baseSinglePtr)); + + CHECK(type::get<ClassSingle6A>() == type::get(baseSingle)); + CHECK(type::get<ClassSingleBase*>() == type::get(&baseSingle)); + + ClassSingle3A instance3A; + CHECK(type::get<ClassSingle3A>() == type::get(instance3A)); + CHECK(type::get<ClassSingle6A>() != type::get(instance3A)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Check is_primitive", "[type]") +{ + CHECK(type::get<int>().is_primitive() == true); + CHECK(type::get<float>().is_primitive() == true); + CHECK(type::get<double>().is_primitive() == true); + CHECK(type::get<char>().is_primitive() == true); + CHECK(type::get<bool>().is_primitive() == true); + + CHECK(type::get<std::string>().is_primitive() == false); + CHECK(type::get<ClassSingle6A>().is_primitive() == false); + CHECK(type::get<ClassMultiple2B>().is_primitive() == false); + CHECK(type::get<FinalClass>().is_primitive() == false); + CHECK(type::get<E_Alignment>().is_primitive() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Check is_class", "[type]") +{ + + CHECK(type::get<std::string>().is_class() == true); + CHECK(type::get<ClassSingle6A>().is_class() == true); + CHECK(type::get<ClassMultiple2B>().is_class() == true); + CHECK(type::get<FinalClass>().is_class() == true); + + CHECK(type::get<int>().is_class() == false); + CHECK(type::get<float>().is_class() == false); + CHECK(type::get<double>().is_class() == false); + CHECK(type::get<char>().is_class() == false); + CHECK(type::get<bool>().is_class() == false); + CHECK(type::get<E_Alignment>().is_class() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Check is_enum", "[type]") +{ + CHECK(type::get<E_Alignment>().is_enumeration() == true); + + CHECK(type::get<int>().is_enumeration() == false); + CHECK(type::get<float>().is_enumeration() == false); + CHECK(type::get<double>().is_enumeration() == false); + CHECK(type::get<char>().is_enumeration() == false); + CHECK(type::get<bool>().is_enumeration() == false); + CHECK(type::get<ClassSingle6A>().is_enumeration() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Check is_pointer", "[type]") +{ + CHECK(type::get<int*>().is_pointer() == true); + CHECK(type::get<ClassSingle6A*>().is_pointer() == true); + CHECK(type::get<int***>().is_pointer() == true); + CHECK(type::get<char*>().is_pointer() == true); + + CHECK(type::get<int>().is_pointer() == false); + CHECK(type::get<float>().is_pointer() == false); + CHECK(type::get<double>().is_pointer() == false); + CHECK(type::get<char>().is_pointer() == false); + CHECK(type::get<bool>().is_pointer() == false); + CHECK(type::get<ClassSingle6A>().is_pointer() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("TypeInfoTests - Check is_array", "[type]") +{ + CHECK(type::get<int[10]>().is_array() == true); + CHECK(type::get<char[10]>().is_array() == true); + + CHECK(type::get<int>().is_array() == false); + CHECK(type::get<float>().is_array() == false); + CHECK(type::get<int*>().is_array() == false); + CHECK(type::get<float*>().is_array() == false); + CHECK(type::get<double>().is_array() == false); + CHECK(type::get<char>().is_array() == false); + CHECK(type::get<bool>().is_array() == false); + CHECK(type::get<ClassSingle6A>().is_array() == false); +} + +///////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/test/test_type.h b/src/test/test_type.h new file mode 100644 index 00000000..2bc548a8 --- /dev/null +++ b/src/test/test_type.h @@ -0,0 +1,26 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ \ No newline at end of file diff --git a/src/test/test_variant.cpp b/src/test/test_variant.cpp new file mode 100644 index 00000000..84f8b1e7 --- /dev/null +++ b/src/test/test_variant.cpp @@ -0,0 +1,788 @@ +/************************************************************************************ +* * +* Copyright (c) 2014 Axel Menzel <info@axelmenzel.de> * +* * +* This file is part of RTTR (Run Time Type Reflection) * +* License: MIT License * +* * +* Permission is hereby granted, free of charge, to any person obtaining * +* a copy of this software and associated documentation files (the "Software"), * +* to deal in the Software without restriction, including without limitation * +* the rights to use, copy, modify, merge, publish, distribute, sublicense, * +* and/or sell copies of the Software, and to permit persons to whom the * +* Software is furnished to do so, subject to the following conditions: * +* * +* The above copyright notice and this permission notice shall be included in * +* all copies or substantial portions of the Software. * +* * +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * +* SOFTWARE. * +* * +*************************************************************************************/ + +#include "catch.hpp" + +#include <rttr/reflect> + +#include <vector> +#include <map> +#include <string> + +using namespace rttr; +using namespace std; + +template<class T> +typename std::enable_if<!std::numeric_limits<T>::is_integer, bool>::type + almost_equal(T x, T y) +{ + return std::abs(x - y) <= std::numeric_limits<T>::epsilon(); +} + + +struct point +{ + point(int x, int y) : _x(x), _y(y) {} + int _x; + int _y; +}; + +RTTR_DECLARE_TYPE(point) + +struct vector2d +{ + vector2d(int x, int y) : _x(x), _y(y) {} + int _x; + int _y; +}; + +RTTR_DECLARE_TYPE(vector2d) + + +static std::string convert_to_string(const point& p, bool& ok) +{ + ok = true; + return std::to_string(p._x) + ", " + std::to_string(p._y); +} + +static vector2d convert_to_vector(const point& p, bool& ok) +{ + ok = true; + return vector2d(p._x, p._y); +} + +struct base +{ + int dummy; + RTTR_ENABLE() +}; +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(base) + +struct derived : virtual base +{ + double dummy2; + RTTR_ENABLE(base) +}; + +RTTR_DECLARE_STANDARD_TYPE_VARIANTS(derived) +RTTR_DECLARE_TYPE(derived**) + +RTTR_REGISTER +{ + type::register_converter_func(convert_to_string); + type::register_converter_func(convert_to_vector); +} + +RTTR_DECLARE_TYPE(int[10]) +RTTR_DECLARE_TYPE(int[2][10]) +RTTR_DECLARE_TYPE(int(*)[2][10]) +RTTR_DECLARE_TYPE(void**) + +TEST_CASE("variant test - BasicTests", "[variant]") +{ + using namespace rttr; + + SECTION("simple basic check") + { + variant var = string("hello world"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.is_type<string>() == true); + const int value = 12; + variant foo(&value); + const int* result = foo.get_value<const int*>(); + REQUIRE(result == &value); + + var = 42; + REQUIRE(var.is_type<int>() == true); + + var = "hello char array"; + REQUIRE(var.is_type<char[17]>() == true); + + var = 42.0; + REQUIRE(var.is_type<double>() == true); + } + + SECTION("extracting tests") + { + variant var = string("hello world"); + REQUIRE(var.get_value<string>() == string("hello world")); + + var = 42; + REQUIRE(var.get_value<int>() == 42); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant conversion - to bool", "[variant]") +{ + SECTION("bool to bool") + { + variant var = true; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + + REQUIRE(var.convert<bool>() == true); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == true); + + var = false; + REQUIRE(var.to_bool() == false); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == false); + } + + SECTION("char to bool") + { + variant var = "true"; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + REQUIRE(var.convert<bool>() == true); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == true); + + var = "fdsfsdf"; + REQUIRE(var.to_bool() == true); + REQUIRE(var.convert<bool>() == true); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == true); + + var = "false"; + REQUIRE(var.to_bool() == false); + var = "false "; + REQUIRE(var.to_bool() == false); + var = " false "; + REQUIRE(var.to_bool() == false); + var = " FALSE "; + REQUIRE(var.to_bool() == false); + var = " \n FALSE\n"; + REQUIRE(var.to_bool() == false); + + REQUIRE(var.convert<bool>() == false); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == false); + } + + SECTION("std::string to bool") + { + variant var = std::string("true"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + + var = std::string("fdsfsdf"); + REQUIRE(var.to_bool() == true); + + var = std::string("false"); + REQUIRE(var.to_bool() == false); + var = std::string("false "); + REQUIRE(var.to_bool() == false); + var = std::string(" false "); + REQUIRE(var.to_bool() == false); + var = std::string(" FALSE "); + REQUIRE(var.to_bool() == false); + var = std::string(" \n FALSE\n"); + REQUIRE(var.to_bool() == false); + } + + SECTION("int to bool") + { + variant var = 1; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + REQUIRE(var.convert<bool>() == true); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == true); + + var = 0; + REQUIRE(var.to_bool() == false); + REQUIRE(var.convert<bool>() == false); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == false); + } + + SECTION("float to bool") + { + variant var = 1.0f; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + REQUIRE(var.convert<bool>() == true); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == true); + + var = 0.0f; + REQUIRE(var.to_bool() == false); + + var = 1.0f / 10000000.0f; + REQUIRE(var.to_bool() == false); + REQUIRE(var.convert<bool>() == false); + REQUIRE(var.convert(type::get<bool>()) == true); + REQUIRE(var.get_value<bool>() == false); + } + + SECTION("double to bool") + { + variant var = 1.0; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_bool() == true); + + var = 0.0; + REQUIRE(var.to_bool() == false); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant conversion - to int", "[variant]") +{ + SECTION("int to int") + { + variant var = 12; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 12); + REQUIRE(var.convert<int>() == 12); + REQUIRE(var.convert(type::get<int>()) == true); + REQUIRE(var.get_value<int>() == 12); + + var = -23; + REQUIRE(var.to_int() == -23); + REQUIRE(var.convert<int>() == -23); + REQUIRE(var.convert(type::get<int>()) == true); + REQUIRE(var.get_value<int>() == -23); + } + + SECTION("char to int") + { + variant var = "23"; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 23); + REQUIRE(var.convert<int>() == 23); + REQUIRE(var.convert(type::get<int>()) == true); + REQUIRE(var.get_value<int>() == 23); + + var = "-12"; + REQUIRE(var.to_int() == -12); + + var = "text 34 and text"; + bool ok = false; + REQUIRE(var.to_int(&ok) == 0); + REQUIRE(ok == false); + + var = "34 and text"; + ok = false; + REQUIRE(var.to_int(&ok) == 0); + REQUIRE(ok == false); + REQUIRE(var.convert<int>() == 0); + REQUIRE(var.convert(type::get<int>()) == false); + } + + SECTION("std::string to int") + { + variant var = std::string("23"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 23); + REQUIRE(var.convert<int>() == 23); + REQUIRE(var.convert(type::get<int>()) == true); + REQUIRE(var.get_value<int>() == 23); + + var = std::string("-12"); + REQUIRE(var.to_int() == -12); + + var = std::string("text 34 and text"); + bool ok = false; + REQUIRE(var.to_int(&ok) == 0); + REQUIRE(ok == false); + + var = std::string("34 and text"); + ok = false; + REQUIRE(var.to_int(&ok) == 0); + REQUIRE(ok == false); + } + + SECTION("bool to int") + { + variant var = true; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 1); + + var = false; + REQUIRE(var.to_int() == 0); + } + + SECTION("float to int") + { + variant var = 1.5f; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 1); + + var = 3.1423f; + REQUIRE(var.to_int() == 3); + + var = 0.0f; + REQUIRE(var.to_int() == 0); + } + + SECTION("double to int") + { + variant var = 1.5; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_int() == 1); + + var = 3.1423f; + REQUIRE(var.to_int() == 3); + + var = 0.0; + REQUIRE(var.to_int() == 0); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant conversion - to std::string", "[variant]") +{ + SECTION("int to std::string") + { + variant var = 12; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "12"); + REQUIRE(var.convert<std::string>() == "12"); + REQUIRE(var.convert(type::get<std::string>()) == true); + REQUIRE(var.get_value<std::string>() == "12"); + + var = -23; + REQUIRE(var.to_string() == "-23"); + } + + SECTION("char to std::string") + { + variant var = "text"; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "text"); + + var = "text 42"; + REQUIRE(var.to_string() == "text 42"); + } + + SECTION("std::string to std::string") + { + variant var = std::string("23"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "23"); + + var = std::string("-12"); + REQUIRE(var.to_string() == "-12"); + } + + SECTION("bool to std::string") + { + variant var = true; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "true"); + + var = false; + REQUIRE(var.to_string() == "false"); + } + + SECTION("float to std::string") + { + variant var = 1.567f; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "1.567"); + + var = 3.12345678f; + REQUIRE(var.to_string() == "3.12346"); + + var = 0.0f; + REQUIRE(var.to_string() == "0"); + } + + SECTION("double to std::string") + { + variant var = 1.567; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_string() == "1.567"); + + var = 3.12345678; + REQUIRE(var.to_string() == "3.12345678"); + + var = 0.0; + REQUIRE(var.to_string() == "0"); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant conversion - to float", "[variant]") +{ + SECTION("int to float") + { + variant var = 12; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_float() == 12.0f); + REQUIRE(var.convert<float>() == 12.0f); + REQUIRE(var.convert(type::get<float>()) == true); + REQUIRE(var.get_value<float>() == 12.0f); + + var = -23; + REQUIRE(var.to_float() == -23.0f); + } + + SECTION("char to float") + { + variant var = "23.0"; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_float() == 23.0f); + REQUIRE(var.convert<float>() == 23.0f); + REQUIRE(var.convert(type::get<float>()) == true); + REQUIRE(var.get_value<float>() == 23.0f); + + var = "text 42"; + bool ok = false; + REQUIRE(var.to_float(&ok) == 0); + REQUIRE(ok == false); + + var = "1.23456"; + ok = false; + REQUIRE(var.to_float(&ok) == 1.23456f); + REQUIRE(ok == true); + + var = "1.23456 Text"; + ok = false; + REQUIRE(var.to_float(&ok) == 0); + REQUIRE(ok == false); + + } + + SECTION("std::string to float") + { + variant var = std::string("23.0"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_float() == 23.0f); + + var = std::string("text 42"); + bool ok = false; + REQUIRE(var.to_float(&ok) == 0); + REQUIRE(ok == false); + + var = std::string("1.23456"); + ok = false; + REQUIRE(var.to_float(&ok) == 1.23456f); + REQUIRE(ok == true); + + var = std::string("1.23456 Text"); + ok = false; + REQUIRE(var.to_float(&ok) == 0); + REQUIRE(ok == false); + } + + SECTION("bool to float") + { + variant var = true; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_float() == 1.0f); + + var = false; + REQUIRE(var.to_float() == 0.0f); + } + + SECTION("float to float") + { + variant var = 1.567f; + REQUIRE(var.is_valid() == true); + REQUIRE(almost_equal(var.to_float(), 1.567f) == true); + + var = 3.12345678f; + REQUIRE(almost_equal(var.to_float(), 3.1234567f) == true); + + var = 0.0f; + REQUIRE(almost_equal(var.to_float(), 0.0f) == true); + } + + SECTION("double to float") + { + variant var = 1.567; + REQUIRE(var.is_valid() == true); + REQUIRE(almost_equal(var.to_float(), 1.567f) == true); + + var = 3.12345678; + REQUIRE(almost_equal(var.to_float(), 3.1234567f) == true); + + var = 0.0; + REQUIRE(almost_equal(var.to_float(), 0.0f) == true); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant conversion - to double", "[variant]") +{ + SECTION("int to double") + { + variant var = 12; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_double() == 12.0); + REQUIRE(var.convert<double>() == 12.0); + REQUIRE(var.convert(type::get<double>()) == true); + REQUIRE(var.get_value<double>() == 12.0); + + var = -23; + REQUIRE(var.to_double() == -23.0); + } + + SECTION("char to double") + { + variant var = "23.0"; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_double() == 23.0); + + var = "text 42"; + bool ok = false; + REQUIRE(var.to_double(&ok) == 0); + REQUIRE(ok == false); + + var = "1.23456"; + ok = false; + REQUIRE(var.to_double(&ok) == 1.23456); + REQUIRE(ok == true); + + var = "1.23456 Text"; + ok = false; + REQUIRE(var.to_double(&ok) == 0.0); + REQUIRE(ok == false); + } + + SECTION("std::string to double") + { + variant var = std::string("23.0"); + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_double() == 23.0); + + var = std::string("text 42"); + bool ok = false; + REQUIRE(var.to_double(&ok) == 0.0); + REQUIRE(ok == false); + + var = std::string("1.23456"); + ok = false; + REQUIRE(var.to_double(&ok) == 1.23456); + REQUIRE(ok == true); + + var = std::string("1.23456 Text"); + ok = false; + REQUIRE(var.to_double(&ok) == 0.0); + REQUIRE(ok == false); + } + + SECTION("bool to double") + { + variant var = true; + REQUIRE(var.is_valid() == true); + REQUIRE(var.to_double() == 1.0); + + var = false; + REQUIRE(var.to_double() == 0.0); + } + + SECTION("float to double") + { + variant var = 1.567f; + REQUIRE(var.is_valid() == true); + REQUIRE(almost_equal(var.to_double(), 1.5670000314712524) == true); + + var = 3.123456f; + REQUIRE(almost_equal(var.to_double(), 3.1234560012817383) == true); + + var = 0.0f; + REQUIRE(almost_equal(var.to_double(), 0.0) == true); + } + + SECTION("double to double") + { + variant var = 1.567; + REQUIRE(var.is_valid() == true); + REQUIRE(almost_equal(var.to_double(), 1.567) == true); + + var = 3.12345678; + REQUIRE(almost_equal(var.to_double(), 3.12345678) == true); + + var = 0.0; + REQUIRE(almost_equal(var.to_double(), 0.0) == true); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant test - convert custom types", "[variant]") +{ + variant var = point(12, 34); + REQUIRE(var.is_type<point>() == true); + bool ok = false; + std::string text = var.to_string(&ok); + REQUIRE(ok == true); + + REQUIRE(var.can_convert<std::string>() == true); + REQUIRE(var.can_convert<vector2d>() == true); + + REQUIRE(var.convert(type::get<std::string>()) == true); + REQUIRE(var.is_type<std::string>() == true); + REQUIRE(var.get_value<std::string>() == "12, 34"); + + // convert to other custom type + var = point(12, 34); + bool ret = var.convert(type::get<vector2d>()); + REQUIRE(ret == true); + REQUIRE(var.is_type<vector2d>() == true); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant test - convert internal", "[variant]") +{ + variant var = 23; + bool ret = var.convert(type::get<std::string>()); + REQUIRE(ret == true); + REQUIRE(var.is_type<std::string>() == true); + + derived* d = new derived; + base* b = d; + var = b; + REQUIRE(var.can_convert(type::get<derived>()) == false); + REQUIRE(var.can_convert(type::get<derived*>()) == true); + + bool could_convert = var.convert(type::get<derived**>()); + REQUIRE(could_convert == false); + + could_convert = var.convert(type::get<derived*>()); + REQUIRE(could_convert == true); + REQUIRE(var.is_type<derived*>() == true); + REQUIRE(var.get_value<derived*>() == d); + REQUIRE(var.convert<base*>() == b); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant test - array", "[variant]") +{ + SECTION("test raw array") + { + int obj[2][10] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + auto foo = &obj; + variant_array int_array = foo; + REQUIRE(int_array.is_valid() == true); + + // check meta information + REQUIRE(int_array.get_type() == type::get(&obj)); + REQUIRE(int_array.get_rank() == 2); + REQUIRE(int_array.get_rank_type(0) == type::get<int[2][10]>()); + REQUIRE(int_array.get_rank_type(1) == type::get<int[10]>()); + REQUIRE(int_array.get_rank_type(2) == type::get<int>()); + REQUIRE(int_array.get_rank_type(3).is_valid() == false); + + REQUIRE(int_array.is_dynamic() == false); + REQUIRE(int_array.get_size() == 2); + REQUIRE(int_array.get_size(0) == 10); + REQUIRE(int_array.get_size(1) == 10); + + int sub_array_int[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + for (std::size_t i = 0; i < int_array.get_size(); ++i) + int_array.set_value(i, sub_array_int); + + REQUIRE(obj[0][5] == 5); + REQUIRE(obj[1][9] == 9); + + for (std::size_t i = 0; i < int_array.get_size(); ++i) + for (std::size_t j = 0; j < int_array.get_size(i); ++j) + int_array.set_value(i, j, -1); + + REQUIRE(obj[0][5] == -1); + REQUIRE(obj[1][9] == -1); + + bool inserted = int_array.insert_value(0, sub_array_int); + REQUIRE(inserted == false); + + bool removed = int_array.remove_value(0); + REQUIRE(removed == false); + } + + SECTION("test dynamic array") + { + std::vector<int> vec(50, 0); + variant var = &vec; + variant_array vec_array = var.to_array(); + REQUIRE(vec_array.is_valid() == true); + REQUIRE(vec_array.is_dynamic() == true); + REQUIRE(vec_array.get_rank() == 1); + REQUIRE(vec_array.get_rank_type(0) == type::get(vec)); + REQUIRE(vec_array.get_rank_type(1) == type::get<int>()); + + REQUIRE(vec_array.get_size() == 50); + REQUIRE(vec_array.get_size(0) == 0); // invalid + + vec_array.set_size(70); + REQUIRE(vec_array.get_size() == 70); + vec_array.set_size(70, 0); + REQUIRE(vec_array.get_size() == 70); + REQUIRE(vec_array.get_size(0) == 0); + + vec_array.set_value(50, 23); + variant ret = vec_array.get_value(50); + REQUIRE(ret.is_type<int>() == true); + REQUIRE(ret.get_value<int>() == 23); + + bool inserted = vec_array.insert_value(50, 42); + REQUIRE(inserted == true); + ret = vec_array.get_value(50); + REQUIRE(ret.get_value<int>() == 42); + + ret = vec_array.get_value(51); + REQUIRE(ret.get_value<int>() == 23); + REQUIRE(vec_array.get_size() == 71); + + bool removed = vec_array.remove_value(50); + REQUIRE(removed == true); + REQUIRE(vec_array.get_size() == 70); + ret = vec_array.get_value(50); + REQUIRE(ret.get_value<int>() == 23); + } + + SECTION("test const array") + { + const std::vector<int> vec(50, 0); + variant var = &vec; + variant_array vec_array = var.to_array(); + // pointer to const array cannot be changed + REQUIRE(vec_array.set_size(0) == false); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +TEST_CASE("variant_array test - array", "[variant]") +{ + variant_array array = std::vector<int>(50, 0); +} + +///////////////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/src/test/test_variant.h b/src/test/test_variant.h new file mode 100644 index 00000000..e69de29b