diff --git a/CMakeLists.txt b/CMakeLists.txt index b2e9ed1d..e496a3f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -155,7 +155,7 @@ else() endif() if(MSVC) - add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) + add_definitions(/WX /W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) if(MSVC_VERSION STREQUAL "1800") # VS2013 doesn't have magic statics @@ -175,10 +175,10 @@ if(MSVC) # how to workaround or fix the error. So I'm disabling it globally. add_definitions(/wd4503) else() - add_definitions(-Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type) + add_definitions(-Werror -Wall -Wextra -Wconversion -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wcast-qual -Wunused -Woverloaded-virtual -Wno-noexcept-type -Wpedantic -Werror=return-type) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef -Wno-double-promotion) + add_definitions(-Weverything -Wno-c++98-compat-pedantic -Wno-c++98-compat -Wno-documentation -Wno-switch-enum -Wno-weak-vtables -Wno-missing-prototypes -Wno-padded -Wno-missing-noreturn -Wno-exit-time-destructors -Wno-documentation-unknown-command -Wno-unused-template -Wno-undef -Wno-double-promotion -Wno-switch-default -Wno-nrvo -Wno-shadow-uncaptured-local -Wno-unsafe-buffer-usage-in-libc-call -Wno-c++20-extensions -Wno-unknown-warning-option -Wno-poison-system-directories -Wno-c++20-compat -Wno-c++17-compat) else() add_definitions(-Wnoexcept) endif() @@ -390,8 +390,16 @@ if(BUILD_TESTING) ) if(NOT UNIT_TEST_LIGHT) + add_library(catch2 STATIC unittests/catch_amalgamated.cpp) + if(NOT MSVC) + target_compile_options(catch2 PRIVATE -Wno-conversion -Wno-noexcept -Wno-maybe-uninitialized) + endif() + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + target_compile_options(catch2 PUBLIC -Wno-unknown-warning-option -Wno-covered-switch-default -Wno-disabled-macro-expansion -Wno-unsafe-buffer-usage -Wno-unused-macros) + endif() + add_executable(compiled_tests unittests/compiled_tests.cpp) - target_link_libraries(compiled_tests ${LIBS} ${CHAISCRIPT_LIBS}) + target_link_libraries(compiled_tests catch2 ${LIBS} ${CHAISCRIPT_LIBS}) catch_discover_tests(compiled_tests TEST_PREFIX "compiled.") add_executable(static_chaiscript_test unittests/static_chaiscript.cpp) @@ -403,7 +411,7 @@ if(BUILD_TESTING) add_test(NAME Boxed_Cast_Test COMMAND boxed_cast_test) add_executable(type_info_test unittests/type_info_test.cpp) - target_link_libraries(type_info_test ${LIBS}) + target_link_libraries(type_info_test catch2 ${LIBS}) add_test(NAME Type_Info_Test COMMAND type_info_test) add_executable(c_linkage_test unittests/c_linkage_test.cpp) diff --git a/cmake/Catch.cmake b/cmake/Catch.cmake index 486e3233..fcdebd60 100644 --- a/cmake/Catch.cmake +++ b/cmake/Catch.cmake @@ -33,6 +33,13 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. [TEST_SUFFIX suffix] [PROPERTIES name1 value1...] [TEST_LIST var] + [REPORTER reporter] + [OUTPUT_DIR dir] + [OUTPUT_PREFIX prefix] + [OUTPUT_SUFFIX suffix] + [DISCOVERY_MODE ] + [SKIP_IS_FAILURE] + [ADD_TAGS_AS_LABELS] ) ``catch_discover_tests`` sets up a post-build command on the test executable @@ -90,86 +97,222 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``. executable is being used in multiple calls to ``catch_discover_tests()``. Note that this variable is only available in CTest. + ``REPORTER reporter`` + Use the specified reporter when running the test case. The reporter will + be passed to the Catch executable as ``--reporter reporter``. + + ``OUTPUT_DIR dir`` + If specified, the parameter is passed along as + ``--out dir/`` to Catch executable. The actual file name is the + same as the test name. This should be used instead of + ``EXTRA_ARGS --out foo`` to avoid race conditions writing the result output + when using parallel test execution. + + ``OUTPUT_PREFIX prefix`` + May be used in conjunction with ``OUTPUT_DIR``. + If specified, ``prefix`` is added to each output file name, like so + ``--out dir/prefix``. + + ``OUTPUT_SUFFIX suffix`` + May be used in conjunction with ``OUTPUT_DIR``. + If specified, ``suffix`` is added to each output file name, like so + ``--out dir/suffix``. This can be used to add a file extension to + the output e.g. ".xml". + + ``DL_PATHS path...`` + Specifies paths that need to be set for the dynamic linker to find shared + libraries/DLLs when running the test executable (PATH/LD_LIBRARY_PATH respectively). + These paths will both be set when retrieving the list of test cases from the + test executable and when the tests are executed themselves. This requires + cmake/ctest >= 3.22. + + ``DL_FRAMEWORK_PATHS path...`` + Specifies paths that need to be set for the dynamic linker to find libraries + packaged as frameworks on Apple platforms when running the test executable + (DYLD_FRAMEWORK_PATH). These paths will both be set when retrieving the list + of test cases from the test executable and when the tests are executed themselves. + This requires cmake/ctest >= 3.22. + + ``DISCOVERY_MODE mode`` + Provides control over when ``catch_discover_tests`` performs test discovery. + By default, ``POST_BUILD`` sets up a post-build command to perform test discovery + at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD`` + behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until + just prior to test execution. This way test discovery occurs in the target environment + where the test has a better chance at finding appropriate runtime dependencies. + + ``DISCOVERY_MODE`` defaults to the value of the + ``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when + calling ``catch_discover_tests``. This provides a mechanism for globally selecting + a preferred test discovery behavior without having to modify each call site. + + ``SKIP_IS_FAILURE`` + Disables skipped test detection. + + ``ADD_TAGS_AS_LABELS`` + Adds all test tags as CTest labels. + #]=======================================================================] #------------------------------------------------------------------------------ function(catch_discover_tests TARGET) + cmake_parse_arguments( "" - "" - "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST" - "TEST_SPEC;EXTRA_ARGS;PROPERTIES" + "SKIP_IS_FAILURE;ADD_TAGS_AS_LABELS" + "TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE" + "TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS;DL_FRAMEWORK_PATHS" ${ARGN} ) + if(${CMAKE_VERSION} VERSION_LESS "3.19") + message(FATAL_ERROR "This script requires JSON support from CMake version 3.19 or greater.") + endif() + if(NOT _WORKING_DIRECTORY) set(_WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") endif() if(NOT _TEST_LIST) set(_TEST_LIST ${TARGET}_TESTS) endif() + if(_DL_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") + message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22") + endif() + if(_DL_FRAMEWORK_PATHS AND ${CMAKE_VERSION} VERSION_LESS "3.22.0") + message(FATAL_ERROR "The DL_FRAMEWORK_PATHS option requires at least cmake 3.22") + endif() + if(NOT _DISCOVERY_MODE) + if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE) + set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD") + endif() + set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE}) + endif() + if(NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$") + message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}") + endif() ## Generate a unique name based on the extra arguments - string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS}") + string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}") string(SUBSTRING ${args_hash} 0 7 args_hash) # Define rule to generate test list for aforementioned test executable - set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") - set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") + set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}") + set(ctest_include_file "${ctest_file_base}_include.cmake") + set(ctest_tests_file "${ctest_file_base}_tests.cmake") + get_property(crosscompiling_emulator TARGET ${TARGET} PROPERTY CROSSCOMPILING_EMULATOR ) - add_custom_command( - TARGET ${TARGET} POST_BUILD - BYPRODUCTS "${ctest_tests_file}" - COMMAND "${CMAKE_COMMAND}" - -D "TEST_TARGET=${TARGET}" - -D "TEST_EXECUTABLE=$" - -D "TEST_EXECUTOR=${crosscompiling_emulator}" - -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" - -D "TEST_SPEC=${_TEST_SPEC}" - -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" - -D "TEST_PROPERTIES=${_PROPERTIES}" - -D "TEST_PREFIX=${_TEST_PREFIX}" - -D "TEST_SUFFIX=${_TEST_SUFFIX}" - -D "TEST_LIST=${_TEST_LIST}" - -D "CTEST_FILE=${ctest_tests_file}" - -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" - VERBATIM - ) + if(NOT _SKIP_IS_FAILURE) + set(_PROPERTIES ${_PROPERTIES} SKIP_RETURN_CODE 4) + endif() - file(WRITE "${ctest_include_file}" - "if(EXISTS \"${ctest_tests_file}\")\n" - " include(\"${ctest_tests_file}\")\n" - "else()\n" - " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" - "endif()\n" - ) - - if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") - # Add discovered tests to directory TEST_INCLUDE_FILES - set_property(DIRECTORY - APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + if(_DISCOVERY_MODE STREQUAL "POST_BUILD") + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TEST_TARGET=${TARGET}" + -D "TEST_EXECUTABLE=$" + -D "TEST_EXECUTOR=${crosscompiling_emulator}" + -D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}" + -D "TEST_SPEC=${_TEST_SPEC}" + -D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}" + -D "TEST_PROPERTIES=${_PROPERTIES}" + -D "TEST_PREFIX=${_TEST_PREFIX}" + -D "TEST_SUFFIX=${_TEST_SUFFIX}" + -D "TEST_LIST=${_TEST_LIST}" + -D "TEST_REPORTER=${_REPORTER}" + -D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}" + -D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}" + -D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}" + -D "TEST_DL_PATHS=${_DL_PATHS}" + -D "TEST_DL_FRAMEWORK_PATHS=${_DL_FRAMEWORK_PATHS}" + -D "CTEST_FILE=${ctest_tests_file}" + -D "ADD_TAGS_AS_LABELS=${_ADD_TAGS_AS_LABELS}" + -P "${_CATCH_DISCOVER_TESTS_SCRIPT}" + VERBATIM ) - else() - # Add discovered tests as directory TEST_INCLUDE_FILE if possible - get_property(test_include_file_set DIRECTORY PROPERTY TEST_INCLUDE_FILE SET) - if (NOT ${test_include_file_set}) - set_property(DIRECTORY - PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}" + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" + "endif()\n" + ) + + elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") + + get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL + PROPERTY GENERATOR_IS_MULTI_CONFIG + ) + + if(GENERATOR_IS_MULTI_CONFIG) + set(ctest_tests_file "${ctest_file_base}_tests-$.cmake") + endif() + + string(CONCAT ctest_include_content + "if(EXISTS \"$\")" "\n" + " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" + " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$\" OR\n" + " NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n" + " include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n" + " catch_discover_tests_impl(" "\n" + " TEST_EXECUTABLE" " [==[" "$" "]==]" "\n" + " TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n" + " TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n" + " TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n" + " TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n" + " TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n" + " TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n" + " TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n" + " TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n" + " TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n" + " TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n" + " TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n" + " TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n" + " CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n" + " TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n" + " TEST_DL_FRAMEWORK_PATHS" " [==[" "${_DL_FRAMEWORK_PATHS}" "]==]" "\n" + " ADD_TAGS_AS_LABELS" " [==[" "${_ADD_TAGS_AS_LABELS}" "]==]" "\n" + " )" "\n" + " endif()" "\n" + " include(\"${ctest_tests_file}\")" "\n" + "else()" "\n" + " add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n" + "endif()" "\n" + ) + + if(GENERATOR_IS_MULTI_CONFIG) + foreach(_config ${CMAKE_CONFIGURATION_TYPES}) + file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $) + endforeach() + string(CONCAT ctest_include_multi_content + "if(NOT CTEST_CONFIGURATION_TYPE)" "\n" + " message(\"No configuration for testing specified, use '-C '.\")" "\n" + "else()" "\n" + " include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n" + "endif()" "\n" ) + file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}") else() - message(FATAL_ERROR - "Cannot set more than one TEST_INCLUDE_FILE" - ) + file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}") + file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")") endif() endif() + # Add discovered tests to directory TEST_INCLUDE_FILES + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + endfunction() ############################################################################### set(_CATCH_DISCOVER_TESTS_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake + CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file" ) diff --git a/cmake/CatchAddTests.cmake b/cmake/CatchAddTests.cmake index 3575a35c..4c27f479 100644 --- a/cmake/CatchAddTests.cmake +++ b/cmake/CatchAddTests.cmake @@ -1,18 +1,12 @@ # Distributed under the OSI-approved BSD 3-Clause License. See accompanying # file Copyright.txt or https://cmake.org/licensing for details. -set(prefix "${TEST_PREFIX}") -set(suffix "${TEST_SUFFIX}") -set(spec ${TEST_SPEC}) -set(extra_args ${TEST_EXTRA_ARGS}) -set(properties ${TEST_PROPERTIES}) -set(script) -set(suite) -set(tests) - function(add_command NAME) set(_args "") - foreach(_arg ${ARGN}) + # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments + math(EXPR _last_arg ${ARGC}-1) + foreach(_n RANGE 1 ${_last_arg}) + set(_arg "${ARGV${_n}}") if(_arg MATCHES "[^-./:a-zA-Z0-9_]") set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument else() @@ -22,55 +16,239 @@ function(add_command NAME) set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE) endfunction() -# Run test executable to get list of available tests -if(NOT EXISTS "${TEST_EXECUTABLE}") - message(FATAL_ERROR - "Specified test executable '${TEST_EXECUTABLE}' does not exist" +function(catch_discover_tests_impl) + + cmake_parse_arguments( + "" + "" + "TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE" + "TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR;TEST_DL_PATHS;TEST_DL_FRAMEWORK_PATHS;ADD_TAGS_AS_LABELS" + ${ARGN} + ) + + set(add_tags "${_ADD_TAGS_AS_LABELS}") + set(prefix "${_TEST_PREFIX}") + set(suffix "${_TEST_SUFFIX}") + set(spec ${_TEST_SPEC}) + set(extra_args ${_TEST_EXTRA_ARGS}) + set(properties ${_TEST_PROPERTIES}) + set(reporter ${_TEST_REPORTER}) + set(output_dir ${_TEST_OUTPUT_DIR}) + set(output_prefix ${_TEST_OUTPUT_PREFIX}) + set(output_suffix ${_TEST_OUTPUT_SUFFIX}) + set(dl_paths ${_TEST_DL_PATHS}) + set(dl_framework_paths ${_TEST_DL_FRAMEWORK_PATHS}) + set(environment_modifications "") + set(script) + set(suite) + set(tests) + + if(WIN32) + set(dl_paths_variable_name PATH) + elseif(APPLE) + set(dl_paths_variable_name DYLD_LIBRARY_PATH) + else() + set(dl_paths_variable_name LD_LIBRARY_PATH) + endif() + + # Run test executable to get list of available tests + if(NOT EXISTS "${_TEST_EXECUTABLE}") + message(FATAL_ERROR + "Specified test executable '${_TEST_EXECUTABLE}' does not exist" + ) + endif() + + if(dl_paths) + cmake_path(CONVERT "$ENV{${dl_paths_variable_name}}" TO_NATIVE_PATH_LIST env_dl_paths) + list(PREPEND env_dl_paths "${dl_paths}") + cmake_path(CONVERT "${env_dl_paths}" TO_NATIVE_PATH_LIST paths) + set(ENV{${dl_paths_variable_name}} "${paths}") + endif() + + if(APPLE AND dl_framework_paths) + cmake_path(CONVERT "$ENV{DYLD_FRAMEWORK_PATH}" TO_NATIVE_PATH_LIST env_dl_framework_paths) + list(PREPEND env_dl_framework_paths "${dl_framework_paths}") + cmake_path(CONVERT "${env_dl_framework_paths}" TO_NATIVE_PATH_LIST paths) + set(ENV{DYLD_FRAMEWORK_PATH} "${paths}") + endif() + + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --reporter json + OUTPUT_VARIABLE listing_output + RESULT_VARIABLE result + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + ) + if(NOT ${result} EQUAL 0) + message(FATAL_ERROR + "Error listing tests from executable '${_TEST_EXECUTABLE}':\n" + " Result: ${result}\n" + " Output: ${listing_output}\n" + ) + endif() + + # Prepare reporter + if(reporter) + set(reporter_arg "--reporter ${reporter}") + + # Run test executable to check whether reporter is available + # note that the use of --list-reporters is not the important part, + # we only want to check whether the execution succeeds with ${reporter_arg} + execute_process( + COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters + OUTPUT_VARIABLE reporter_check_output + RESULT_VARIABLE reporter_check_result + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + ) + if(${reporter_check_result} EQUAL 255) + message(FATAL_ERROR + "\"${reporter}\" is not a valid reporter!\n" + ) + elseif(NOT ${reporter_check_result} EQUAL 0) + message(FATAL_ERROR + "Error checking for reporter in test executable '${_TEST_EXECUTABLE}':\n" + " Result: ${reporter_check_result}\n" + " Output: ${reporter_check_output}\n" + ) + endif() + endif() + + # Prepare output dir + if(output_dir AND NOT IS_ABSOLUTE ${output_dir}) + set(output_dir "${_TEST_WORKING_DIR}/${output_dir}") + if(NOT EXISTS ${output_dir}) + file(MAKE_DIRECTORY ${output_dir}) + endif() + endif() + + if(dl_paths) + foreach(path ${dl_paths}) + cmake_path(NATIVE_PATH path native_path) + list(PREPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}") + endforeach() + endif() + + if(APPLE AND dl_framework_paths) + foreach(path ${dl_framework_paths}) + cmake_path(NATIVE_PATH path native_path) + list(PREPEND environment_modifications "DYLD_FRAMEWORK_PATH=path_list_prepend:${native_path}") + endforeach() + endif() + + # Parse JSON output for list of tests/class names/tags + string(JSON version GET "${listing_output}" "version") + if(NOT version STREQUAL "1") + message(FATAL_ERROR "Unsupported catch output version: '${version}'") + endif() + + # Speed-up reparsing by cutting away unneeded parts of JSON. + string(JSON test_listing GET "${listing_output}" "listings" "tests") + string(JSON num_tests LENGTH "${test_listing}") + + # Exit early if no tests are detected + if(num_tests STREQUAL "0") + file(WRITE "${_CTEST_FILE}" "") + return() + endif() + + # CMake's foreach-RANGE is inclusive, so we have to subtract 1 + math(EXPR num_tests "${num_tests} - 1") + + foreach(idx RANGE ${num_tests}) + string(JSON single_test GET ${test_listing} ${idx}) + string(JSON test_tags GET "${single_test}" "tags") + string(JSON plain_name GET "${single_test}" "name") + + # Escape characters in test case names that would be parsed by Catch2 + # Note that the \ escaping must happen FIRST! Do not change the order. + set(escaped_name "${plain_name}") + foreach(char \\ , [ ] ;) + string(REPLACE ${char} "\\${char}" escaped_name "${escaped_name}") + endforeach(char) + # ...add output dir + if(output_dir) + string(REGEX REPLACE "[^A-Za-z0-9_]" "_" escaped_name_clean "${escaped_name}") + set(output_dir_arg "--out ${output_dir}/${output_prefix}${escaped_name_clean}${output_suffix}") + endif() + + # ...and add to script + add_command(add_test + "${prefix}${plain_name}${suffix}" + ${_TEST_EXECUTOR} + "${_TEST_EXECUTABLE}" + "${escaped_name}" + ${extra_args} + "${reporter_arg}" + "${output_dir_arg}" + ) + add_command(set_tests_properties + "${prefix}${plain_name}${suffix}" + PROPERTIES + WORKING_DIRECTORY "${_TEST_WORKING_DIR}" + ${properties} + ) + + if(add_tags) + string(JSON num_tags LENGTH "${test_tags}") + math(EXPR num_tags "${num_tags} - 1") + set(tag_list "") + if(num_tags GREATER_EQUAL "0") + foreach(tag_idx RANGE ${num_tags}) + string(JSON a_tag GET "${test_tags}" "${tag_idx}") + # Catch2's tags can contain semicolons, which are list element separators + # in CMake, so we have to escape them. Ideally we could use the [=[...]=] + # syntax for this, but CTest currently keeps the square quotes in the label + # name. So we add 2 backslashes to escape it instead. + # **IMPORTANT**: The number of backslashes depends on how many layers + # of CMake the tag goes. If this script is changed, the + # number of backslashes to escape may change as well. + string(REPLACE ";" "\\;" a_tag "${a_tag}") + list(APPEND tag_list "${a_tag}") + endforeach() + + add_command(set_tests_properties + "${prefix}${plain_name}${suffix}" + PROPERTIES + LABELS "${tag_list}" + ) + endif() + endif(add_tags) + + if(environment_modifications) + add_command(set_tests_properties + "${prefix}${plain_name}${suffix}" + PROPERTIES + ENVIRONMENT_MODIFICATION "${environment_modifications}") + endif() + + list(APPEND tests "${prefix}${plain_name}${suffix}") + endforeach() + + # Create a list of all discovered tests, which users may use to e.g. set + # properties on the tests + add_command(set ${_TEST_LIST} ${tests}) + + # Write CTest script + file(WRITE "${_CTEST_FILE}" "${script}") +endfunction() + +if(CMAKE_SCRIPT_MODE_FILE) + catch_discover_tests_impl( + TEST_EXECUTABLE ${TEST_EXECUTABLE} + TEST_EXECUTOR ${TEST_EXECUTOR} + TEST_WORKING_DIR ${TEST_WORKING_DIR} + TEST_SPEC ${TEST_SPEC} + TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS} + TEST_PROPERTIES ${TEST_PROPERTIES} + TEST_PREFIX ${TEST_PREFIX} + TEST_SUFFIX ${TEST_SUFFIX} + TEST_LIST ${TEST_LIST} + TEST_REPORTER ${TEST_REPORTER} + TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR} + TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX} + TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX} + TEST_DL_PATHS ${TEST_DL_PATHS} + TEST_DL_FRAMEWORK_PATHS ${TEST_DL_FRAMEWORK_PATHS} + CTEST_FILE ${CTEST_FILE} + ADD_TAGS_AS_LABELS ${ADD_TAGS_AS_LABELS} ) endif() -execute_process( - COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only - OUTPUT_VARIABLE output - RESULT_VARIABLE result -) -# Catch --list-test-names-only reports the number of tests, so 0 is... surprising -if(${result} EQUAL 0) - message(WARNING - "Test executable '${TEST_EXECUTABLE}' contains no tests!\n" - ) -elseif(${result} LESS 0) - message(FATAL_ERROR - "Error running test executable '${TEST_EXECUTABLE}':\n" - " Result: ${result}\n" - " Output: ${output}\n" - ) -endif() - -string(REPLACE "\n" ";" output "${output}") - -# Parse output -foreach(line ${output}) - set(test ${line}) - # ...and add to script - add_command(add_test - "${prefix}${test}${suffix}" - ${TEST_EXECUTOR} - "${TEST_EXECUTABLE}" - ${test} - ${extra_args} - ) - add_command(set_tests_properties - "${prefix}${test}${suffix}" - PROPERTIES - WORKING_DIRECTORY "${TEST_WORKING_DIR}" - ${properties} - ) - list(APPEND tests "${prefix}${test}${suffix}") -endforeach() - -# Create a list of all discovered tests, which users may use to e.g. set -# properties on the tests -add_command(set ${TEST_LIST} ${tests}) - -# Write CTest script -file(WRITE "${CTEST_FILE}" "${script}") diff --git a/cmake/CatchShardTests.cmake b/cmake/CatchShardTests.cmake new file mode 100644 index 00000000..89666d66 --- /dev/null +++ b/cmake/CatchShardTests.cmake @@ -0,0 +1,72 @@ + +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) + +# SPDX-License-Identifier: BSL-1.0 + +# Supported optional args: +# * SHARD_COUNT - number of shards to split target's tests into +# * REPORTER - reporter spec to use for tests +# * TEST_SPEC - test spec used for filtering tests +function(catch_add_sharded_tests TARGET) + if(${CMAKE_VERSION} VERSION_LESS "3.10.0") + message(FATAL_ERROR "add_sharded_catch_tests only supports CMake versions 3.10.0 and up") + endif() + + cmake_parse_arguments( + "" + "" + "SHARD_COUNT;REPORTER;TEST_SPEC" + "" + ${ARGN} + ) + + if(NOT DEFINED _SHARD_COUNT) + set(_SHARD_COUNT 2) + endif() + + # Generate a unique name based on the extra arguments + string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX} ${_SHARD_COUNT}") + string(SUBSTRING ${args_hash} 0 7 args_hash) + + set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-include-${args_hash}.cmake") + set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-sharded-tests-impl-${args_hash}.cmake") + + file(WRITE "${ctest_include_file}" + "if(EXISTS \"${ctest_tests_file}\")\n" + " include(\"${ctest_tests_file}\")\n" + "else()\n" + " add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n" + "endif()\n" + ) + + set_property(DIRECTORY + APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}" + ) + + set(shard_impl_script_file "${_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT}") + + add_custom_command( + TARGET ${TARGET} POST_BUILD + BYPRODUCTS "${ctest_tests_file}" + COMMAND "${CMAKE_COMMAND}" + -D "TARGET_NAME=${TARGET}" + -D "TEST_BINARY=$" + -D "CTEST_FILE=${ctest_tests_file}" + -D "SHARD_COUNT=${_SHARD_COUNT}" + -D "REPORTER_SPEC=${_REPORTER}" + -D "TEST_SPEC=${_TEST_SPEC}" + -P "${shard_impl_script_file}" + VERBATIM + ) +endfunction() + + +############################################################################### + +set(_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT + ${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake + CACHE INTERNAL "Catch2 full path to CatchShardTestsImpl.cmake helper file" +) diff --git a/cmake/CatchShardTestsImpl.cmake b/cmake/CatchShardTestsImpl.cmake new file mode 100644 index 00000000..83fac688 --- /dev/null +++ b/cmake/CatchShardTestsImpl.cmake @@ -0,0 +1,52 @@ + +# Copyright Catch2 Authors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE.txt or copy at +# https://www.boost.org/LICENSE_1_0.txt) + +# SPDX-License-Identifier: BSL-1.0 + +# Indirection for CatchShardTests that allows us to delay the script +# file generation until build time. + +# Expected args: +# * TEST_BINARY - full path to the test binary to run sharded +# * CTEST_FILE - full path to ctest script file to write to +# * TARGET_NAME - name of the target to shard (used for test names) +# * SHARD_COUNT - number of shards to split the binary into +# Optional args: +# * REPORTER_SPEC - reporter specs to be passed down to the binary +# * TEST_SPEC - test spec to pass down to the test binary + +if(NOT EXISTS "${TEST_BINARY}") + message(FATAL_ERROR + "Specified test binary '${TEST_BINARY}' does not exist" + ) +endif() + +set(other_args "") +if(TEST_SPEC) + set(other_args "${other_args} ${TEST_SPEC}") +endif() +if(REPORTER_SPEC) + set(other_args "${other_args} --reporter ${REPORTER_SPEC}") +endif() + +# foreach RANGE in cmake is inclusive of the end, so we have to adjust it +math(EXPR adjusted_shard_count "${SHARD_COUNT} - 1") + +file(WRITE "${CTEST_FILE}" + "string(RANDOM LENGTH 8 ALPHABET \"0123456789abcdef\" rng_seed)\n" + "\n" + "foreach(shard_idx RANGE ${adjusted_shard_count})\n" + " add_test(${TARGET_NAME}-shard-" [[${shard_idx}]] "/${adjusted_shard_count}\n" + " ${TEST_BINARY}" + " --shard-index " [[${shard_idx}]] + " --shard-count ${SHARD_COUNT}" + " --rng-seed " [[0x${rng_seed}]] + " --order rand" + "${other_args}" + "\n" + " )\n" + "endforeach()\n" +) diff --git a/cmake/ParseAndAddCatchTests.cmake b/cmake/ParseAndAddCatchTests.cmake index cb2846d0..d7c17ace 100644 --- a/cmake/ParseAndAddCatchTests.cmake +++ b/cmake/ParseAndAddCatchTests.cmake @@ -1,9 +1,11 @@ #==================================================================================================# # supported macros # # - TEST_CASE, # +# - TEMPLATE_TEST_CASE # # - SCENARIO, # # - TEST_CASE_METHOD, # # - CATCH_TEST_CASE, # +# - CATCH_TEMPLATE_TEST_CASE # # - CATCH_SCENARIO, # # - CATCH_TEST_CASE_METHOD. # # # @@ -39,9 +41,24 @@ # PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS (Default OFF) # # -- causes CMake to rerun when file with tests changes so that new tests will be discovered # # # +# One can also set (locally) the optional variable OptionalCatchTestLauncher to precise the way # +# a test should be run. For instance to use test MPI, one can write # +# set(OptionalCatchTestLauncher ${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${NUMPROC}) # +# just before calling this ParseAndAddCatchTests function # +# # +# The AdditionalCatchParameters optional variable can be used to pass extra argument to the test # +# command. For example, to include successful tests in the output, one can write # +# set(AdditionalCatchParameters --success) # +# # +# After the script, the ParseAndAddCatchTests_TESTS property for the target, and for each source # +# file in the target is set, and contains the list of the tests extracted from that target, or # +# from that file. This is useful, for example to add further labels or properties to the tests. # +# # #==================================================================================================# -cmake_minimum_required(VERSION 2.8.8) +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.8) + message(FATAL_ERROR "ParseAndAddCatchTests requires CMake 2.8.8 or newer") +endif() option(PARSE_CATCH_TESTS_VERBOSE "Print Catch to CTest parser debug messages" OFF) option(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS "Exclude tests with [!hide], [.] or [.foo] tags" OFF) @@ -49,10 +66,10 @@ option(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME "Add fixture class name to the option(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME "Add target name to the test name" ON) option(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS "Add test file to CMAKE_CONFIGURE_DEPENDS property" OFF) -function(PrintDebugMessage) - if(PARSE_CATCH_TESTS_VERBOSE) - message(STATUS "ParseAndAddCatchTests: ${ARGV}") - endif() +function(ParseAndAddCatchTests_PrintDebugMessage) + if(PARSE_CATCH_TESTS_VERBOSE) + message(STATUS "ParseAndAddCatchTests: ${ARGV}") + endif() endfunction() # This removes the contents between @@ -60,7 +77,7 @@ endfunction() # - full line comments (i.e. // ... ) # contents have been read into '${CppCode}'. # !keep partial line comments -function(RemoveComments CppCode) +function(ParseAndAddCatchTests_RemoveComments CppCode) string(ASCII 2 CMakeBeginBlockComment) string(ASCII 3 CMakeEndBlockComment) string(REGEX REPLACE "/\\*" "${CMakeBeginBlockComment}" ${CppCode} "${${CppCode}}") @@ -72,114 +89,162 @@ function(RemoveComments CppCode) endfunction() # Worker function -function(ParseFile SourceFile TestTarget) - # According to CMake docs EXISTS behavior is well-defined only for full paths. - get_filename_component(SourceFile ${SourceFile} ABSOLUTE) - if(NOT EXISTS ${SourceFile}) - message(WARNING "Cannot find source file: ${SourceFile}") - return() +function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget) + # If SourceFile is an object library, do not scan it (as it is not a file). Exit without giving a warning about a missing file. + if(SourceFile MATCHES "\\\$") + ParseAndAddCatchTests_PrintDebugMessage("Detected OBJECT library: ${SourceFile} this will not be scanned for tests.") + return() + endif() + # According to CMake docs EXISTS behavior is well-defined only for full paths. + get_filename_component(SourceFile ${SourceFile} ABSOLUTE) + if(NOT EXISTS ${SourceFile}) + message(WARNING "Cannot find source file: ${SourceFile}") + return() + endif() + ParseAndAddCatchTests_PrintDebugMessage("parsing ${SourceFile}") + file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) + + # Remove block and fullline comments + ParseAndAddCatchTests_RemoveComments(Contents) + + # Find definition of test names + # https://regex101.com/r/JygOND/1 + string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([ \t\n]*\"[^\"]*\"[ \t\n]*(,[ \t\n]*\"[^\"]*\")?(,[ \t\n]*[^\,\)]*)*\\)[ \t\n]*\{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") + + if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) + ParseAndAddCatchTests_PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") + set_property( + DIRECTORY + APPEND + PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} + ) + endif() + + # check CMP0110 policy for new add_test() behavior + if(POLICY CMP0110) + cmake_policy(GET CMP0110 _cmp0110_value) # new add_test() behavior + else() + # just to be thorough explicitly set the variable + set(_cmp0110_value) + endif() + + foreach(TestName ${Tests}) + # Strip newlines + string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") + + # Get test type and fixture if applicable + string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") + string(REGEX MATCH "(CATCH_)?(TEMPLATE_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") + string(REGEX REPLACE "${TestType}\\([ \t]*" "" TestFixture "${TestTypeAndFixture}") + + # Get string parts of test definition + string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") + + # Strip wrapping quotation marks + string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") + string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") + + # Validate that a test name and tags have been provided + list(LENGTH TestStrings TestStringsLength) + if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) + message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") endif() - PrintDebugMessage("parsing ${SourceFile}") - file(STRINGS ${SourceFile} Contents NEWLINE_CONSUME) - # Remove block and fullline comments - RemoveComments(Contents) + # Assign name and tags + list(GET TestStrings 0 Name) + if("${TestType}" STREQUAL "SCENARIO") + set(Name "Scenario: ${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND "${TestType}" MATCHES "(CATCH_)?TEST_CASE_METHOD" AND TestFixture) + set(CTestName "${TestFixture}:${Name}") + else() + set(CTestName "${Name}") + endif() + if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) + set(CTestName "${TestTarget}:${CTestName}") + endif() + # add target to labels to enable running all tests added from this target + set(Labels ${TestTarget}) + if(TestStringsLength EQUAL 2) + list(GET TestStrings 1 Tags) + string(TOLOWER "${Tags}" Tags) + # remove target from labels if the test is hidden + if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") + list(REMOVE_ITEM Labels ${TestTarget}) + endif() + string(REPLACE "]" ";" Tags "${Tags}") + string(REPLACE "[" "" Tags "${Tags}") + else() + # unset tags variable from previous loop + unset(Tags) + endif() - # Find definition of test names - string(REGEX MATCHALL "[ \t]*(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^\)]+\\)+[ \t\n]*{+[ \t]*(//[^\n]*[Tt][Ii][Mm][Ee][Oo][Uu][Tt][ \t]*[0-9]+)*" Tests "${Contents}") + list(APPEND Labels ${Tags}) - if(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS AND Tests) - PrintDebugMessage("Adding ${SourceFile} to CMAKE_CONFIGURE_DEPENDS property") + set(HiddenTagFound OFF) + foreach(label ${Labels}) + string(REGEX MATCH "^!hide|^\\." result ${label}) + if(result) + set(HiddenTagFound ON) + break() + endif() + endforeach(label) + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_LESS "3.9") + ParseAndAddCatchTests_PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") + else() + ParseAndAddCatchTests_PrintDebugMessage("Adding test \"${CTestName}\"") + if(Labels) + ParseAndAddCatchTests_PrintDebugMessage("Setting labels to ${Labels}") + endif() + + # Escape commas in the test spec + string(REPLACE "," "\\," Name ${Name}) + + # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary, + # only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1 + # And properly introduced in 3.19 with the CMP0110 policy + if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18") + ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to NEW, no need for add_test(\"\") workaround") + else() + ParseAndAddCatchTests_PrintDebugMessage("CMP0110 set to OLD adding \"\" for add_test() workaround") + set(CTestName "\"${CTestName}\"") + endif() + + # Handle template test cases + if("${TestTypeAndFixture}" MATCHES ".*TEMPLATE_.*") + set(Name "${Name} - *") + endif() + + # Add the test and set its properties + add_test(NAME "${CTestName}" COMMAND ${OptionalCatchTestLauncher} $ ${Name} ${AdditionalCatchParameters}) + # Old CMake versions do not document VERSION_GREATER_EQUAL, so we use VERSION_GREATER with 3.8 instead + if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound} AND ${CMAKE_VERSION} VERSION_GREATER "3.8") + ParseAndAddCatchTests_PrintDebugMessage("Setting DISABLED test property") + set_tests_properties("${CTestName}" PROPERTIES DISABLED ON) + else() + set_tests_properties("${CTestName}" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" + LABELS "${Labels}") + endif() set_property( - DIRECTORY + TARGET ${TestTarget} APPEND - PROPERTY CMAKE_CONFIGURE_DEPENDS ${SourceFile} - ) + PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") + set_property( + SOURCE ${SourceFile} + APPEND + PROPERTY ParseAndAddCatchTests_TESTS "${CTestName}") endif() - - foreach(TestName ${Tests}) - # Strip newlines - string(REGEX REPLACE "\\\\\n|\n" "" TestName "${TestName}") - - # Get test type and fixture if applicable - string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)[ \t]*\\([^,^\"]*" TestTypeAndFixture "${TestName}") - string(REGEX MATCH "(CATCH_)?(TEST_CASE_METHOD|SCENARIO|TEST_CASE)" TestType "${TestTypeAndFixture}") - string(REPLACE "${TestType}(" "" TestFixture "${TestTypeAndFixture}") - - # Get string parts of test definition - string(REGEX MATCHALL "\"+([^\\^\"]|\\\\\")+\"+" TestStrings "${TestName}") - - # Strip wrapping quotation marks - string(REGEX REPLACE "^\"(.*)\"$" "\\1" TestStrings "${TestStrings}") - string(REPLACE "\";\"" ";" TestStrings "${TestStrings}") - - # Validate that a test name and tags have been provided - list(LENGTH TestStrings TestStringsLength) - if(TestStringsLength GREATER 2 OR TestStringsLength LESS 1) - message(FATAL_ERROR "You must provide a valid test name and tags for all tests in ${SourceFile}") - endif() - - # Assign name and tags - list(GET TestStrings 0 Name) - if("${TestType}" STREQUAL "SCENARIO") - set(Name "Scenario: ${Name}") - endif() - if(PARSE_CATCH_TESTS_ADD_FIXTURE_IN_TEST_NAME AND TestFixture) - set(CTestName "${TestFixture}:${Name}") - else() - set(CTestName "${Name}") - endif() - if(PARSE_CATCH_TESTS_ADD_TARGET_IN_TEST_NAME) - set(CTestName "${TestTarget}:${CTestName}") - endif() - # add target to labels to enable running all tests added from this target - set(Labels ${TestTarget}) - if(TestStringsLength EQUAL 2) - list(GET TestStrings 1 Tags) - string(TOLOWER "${Tags}" Tags) - # remove target from labels if the test is hidden - if("${Tags}" MATCHES ".*\\[!?(hide|\\.)\\].*") - list(REMOVE_ITEM Labels ${TestTarget}) - endif() - string(REPLACE "]" ";" Tags "${Tags}") - string(REPLACE "[" "" Tags "${Tags}") - endif() - - list(APPEND Labels ${Tags}) - - list(FIND Labels "!hide" IndexOfHideLabel) - set(HiddenTagFound OFF) - foreach(label ${Labels}) - string(REGEX MATCH "^!hide|^\\." result ${label}) - if(result) - set(HiddenTagFound ON) - break() - endif(result) - endforeach(label) - if(PARSE_CATCH_TESTS_NO_HIDDEN_TESTS AND ${HiddenTagFound}) - PrintDebugMessage("Skipping test \"${CTestName}\" as it has [!hide], [.] or [.foo] label") - else() - PrintDebugMessage("Adding test \"${CTestName}\"") - if(Labels) - PrintDebugMessage("Setting labels to ${Labels}") - endif() - - # Add the test and set its properties - add_test(NAME "\"${CTestName}\"" COMMAND ${TestTarget} ${Name} ${AdditionalCatchParameters}) - set_tests_properties("\"${CTestName}\"" PROPERTIES FAIL_REGULAR_EXPRESSION "No tests ran" - LABELS "${Labels}") - endif() - - endforeach() + endforeach() endfunction() # entry point function(ParseAndAddCatchTests TestTarget) - PrintDebugMessage("Started parsing ${TestTarget}") - get_target_property(SourceFiles ${TestTarget} SOURCES) - PrintDebugMessage("Found the following sources: ${SourceFiles}") - foreach(SourceFile ${SourceFiles}) - ParseFile(${SourceFile} ${TestTarget}) - endforeach() - PrintDebugMessage("Finished parsing ${TestTarget}") + message(DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'") + ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}") + get_target_property(SourceFiles ${TestTarget} SOURCES) + ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}") + foreach(SourceFile ${SourceFiles}) + ParseAndAddCatchTests_ParseFile(${SourceFile} ${TestTarget}) + endforeach() + ParseAndAddCatchTests_PrintDebugMessage("Finished parsing ${TestTarget}") endfunction() diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index f50510f0..5dc9ff7a 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -59,10 +59,6 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req #define CHAISCRIPT_MODULE_EXPORT extern "C" #endif -#if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG) -#define CHAISCRIPT_UTF16_UTF32 -#endif - #ifdef _DEBUG #define CHAISCRIPT_DEBUG true #else diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index 15b3d442..5c1e7a48 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -45,11 +45,10 @@ namespace chaiscript { auto lib = std::make_shared(); - const bool no_io = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_IO) != t_opts.end(); const bool no_prelude = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_Prelude) != t_opts.end(); const bool no_json = std::find(t_opts.begin(), t_opts.end(), Library_Options::No_JSON) != t_opts.end(); - bootstrap::Bootstrap::bootstrap(*lib, no_io); + bootstrap::Bootstrap::bootstrap(*lib); bootstrap::standard_library::vector_type>("Vector", *lib); bootstrap::standard_library::string_type("string", *lib); diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index 041af80e..3c15f0ec 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -26,7 +26,7 @@ namespace chaiscript::bootstrap { throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); } else { - return t[index]; + return *std::next(t, static_cast(index)); } }), "[]"); @@ -37,7 +37,7 @@ namespace chaiscript::bootstrap { throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); } else { - return t[index]; + return *std::next(t, static_cast(index)); } }), "[]"); @@ -269,7 +269,7 @@ namespace chaiscript::bootstrap { /// \brief perform all common bootstrap functions for std::string, void and POD types /// \param[in,out] m Module to add bootstrapped functions to /// \param[in] t_no_io If true, skip registering print_string and println_string - static void bootstrap(Module &m, const bool t_no_io = false) { + static void bootstrap(Module &m) { m.add(user_type(), "void"); m.add(user_type(), "bool"); m.add(user_type(), "Object"); @@ -437,11 +437,6 @@ namespace chaiscript::bootstrap { m.add(fun(&Build_Info::compiler_id), "compiler_id"); m.add(fun(&Build_Info::debug_build), "debug_build"); - // print_string and println_string are registered in ChaiScript_Basic::build_eval_system() - // to support per-instance IO redirection via set_print_handler. - // When No_IO is set, the functions are still registered but the default handler - // is a no-op, so users can provide their own print handlers without any stdout output. - m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); m.add(fun(&shared_ptr_unconst_clone), "clone"); diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index 0a373ffb..05faa4af 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -41,7 +41,7 @@ namespace chaiscript { // this is OK, so we're disabling size/and sign type warnings #ifdef CHAISCRIPT_MSVC #pragma warning(push) -#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242) +#pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242 4702) // 4702 is for broken unreachable warning #endif #ifdef __GNUC__ diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index be43905d..2d1eff7e 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -34,7 +34,6 @@ #include "dynamic_object.hpp" #include "proxy_constructors.hpp" #include "proxy_functions.hpp" -#include "short_alloc.hpp" #include "type_conversions.hpp" #include "type_info.hpp" @@ -318,9 +317,6 @@ namespace chaiscript { namespace detail { struct Stack_Holder { - // template - // using SmallVector = std::vector>; - template using SmallVector = std::vector; @@ -377,8 +373,8 @@ namespace chaiscript { Dispatch_Engine(const Dispatch_Engine &) = delete; Dispatch_Engine &operator=(const Dispatch_Engine &) = delete; - Dispatch_Engine(Dispatch_Engine &&) = default; - Dispatch_Engine &operator=(Dispatch_Engine &&) = default; + Dispatch_Engine(Dispatch_Engine &&) = delete; + Dispatch_Engine &operator=(Dispatch_Engine &&) = delete; #ifndef CHAISCRIPT_NO_THREADS /// Track an async thread so it can be joined during destruction @@ -790,13 +786,13 @@ namespace chaiscript { t_loc = uint_fast32_t(funs.first); } - const auto do_attribute_call = [this](int l_num_params, + const auto do_attribute_call = [this](std::size_t l_num_params, Function_Params l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions) -> Boxed_Value { - Function_Params attr_params(l_params.begin(), l_params.begin() + l_num_params); + Function_Params attr_params(l_params.first(l_num_params)); Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); - if (l_num_params < int(l_params.size()) || bv.get_type_info().bare_equal(user_type())) { + if (l_num_params < l_params.size() || bv.get_type_info().bare_equal(user_type())) { struct This_Foist { This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) { @@ -814,16 +810,16 @@ namespace chaiscript { try { auto func = boxed_cast(bv); try { - return (*func)({l_params.begin() + l_num_params, l_params.end()}, l_conversions); + return (*func)(l_params.subspan(l_num_params), l_conversions); } catch (const chaiscript::exception::bad_boxed_cast &) { } catch (const chaiscript::exception::arity_error &) { } catch (const chaiscript::exception::guard_error &) { } - throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, + throw chaiscript::exception::dispatch_error(l_params.subspan(l_num_params), std::vector{boxed_cast(bv)}); } catch (const chaiscript::exception::bad_boxed_cast &) { // unable to convert bv into a Proxy_Function_Base - throw chaiscript::exception::dispatch_error({l_params.begin() + l_num_params, l_params.end()}, + throw chaiscript::exception::dispatch_error(l_params.subspan(l_num_params), std::vector(l_funs.begin(), l_funs.end())); } } else { @@ -873,7 +869,7 @@ namespace chaiscript { if (!functions.empty()) { try { if (is_no_param) { - auto tmp_params = params.to_vector(); + auto tmp_params = std::vector(params.begin(), params.end()); tmp_params.insert(tmp_params.begin() + 1, var(t_name)); return do_attribute_call(2, Function_Params(tmp_params), functions, t_conversions); } else { @@ -948,7 +944,7 @@ namespace chaiscript { const auto &f = this->boxed_cast(params[0]); const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves()); - return const_var(f->call_match(Function_Params(params.begin() + 1, params.end()), convs)); + return const_var(f->call_match(Function_Params(params.subspan(1)), convs)); } /// Dump all system info to stdout diff --git a/include/chaiscript/dispatchkit/function_params.hpp b/include/chaiscript/dispatchkit/function_params.hpp index 9a77c5ee..a1e5aacf 100644 --- a/include/chaiscript/dispatchkit/function_params.hpp +++ b/include/chaiscript/dispatchkit/function_params.hpp @@ -12,55 +12,11 @@ #include "boxed_value.hpp" +#include + namespace chaiscript { - class Function_Params { - public: - constexpr Function_Params(const Boxed_Value *const t_begin, const Boxed_Value *const t_end) - : m_begin(t_begin) - , m_end(t_end) { - } - explicit Function_Params(const Boxed_Value &bv) - : m_begin(&bv) - , m_end(m_begin + 1) { - } - - explicit Function_Params(const std::vector &vec) - : m_begin(vec.empty() ? nullptr : &vec.front()) - , m_end(vec.empty() ? nullptr : &vec.front() + vec.size()) { - } - - template - constexpr explicit Function_Params(const std::array &a) - : m_begin(&a.front()) - , m_end(&a.front() + Size) { - } - - [[nodiscard]] constexpr const Boxed_Value &operator[](const std::size_t t_i) const noexcept { return m_begin[t_i]; } - - [[nodiscard]] constexpr const Boxed_Value *begin() const noexcept { return m_begin; } - - [[nodiscard]] constexpr const Boxed_Value &front() const noexcept { return *m_begin; } - - [[nodiscard]] constexpr const Boxed_Value *end() const noexcept { return m_end; } - - [[nodiscard]] constexpr std::size_t size() const noexcept { return std::size_t(m_end - m_begin); } - - [[nodiscard]] std::vector to_vector() const { return std::vector{m_begin, m_end}; } - - [[nodiscard]] constexpr bool empty() const noexcept { return m_begin == m_end; } - - private: - const Boxed_Value *m_begin = nullptr; - const Boxed_Value *m_end = nullptr; - }; - - // Constructor specialization for array of size 0 - template<> - constexpr Function_Params::Function_Params(const std::array & /* a */) - : m_begin(nullptr) - , m_end(nullptr) { - } + using Function_Params = std::span; } // namespace chaiscript diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index bd29294d..07cb55fa 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -65,7 +65,7 @@ namespace chaiscript { bool operator==(const Param_Types &t_rhs) const noexcept { return m_types == t_rhs.m_types; } std::vector convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const { - auto vals = t_params.to_vector(); + auto vals = std::vector{t_params.begin(), t_params.end()}; const auto dynamic_object_type_info = user_type(); for (size_t i = 0; i < vals.size(); ++i) { const auto &name = m_types[i].first; @@ -682,13 +682,13 @@ namespace chaiscript { public: dispatch_error(const Function_Params &t_parameters, std::vector t_functions) : std::runtime_error("Error with function dispatch") - , parameters(t_parameters.to_vector()) + , parameters(t_parameters.begin(), t_parameters.end()) , functions(std::move(t_functions)) { } dispatch_error(const Function_Params &t_parameters, std::vector t_functions, const std::string &t_desc) : std::runtime_error(t_desc) - , parameters(t_parameters.to_vector()) + , parameters(t_parameters.begin(), t_parameters.end()) , functions(std::move(t_functions)) { } diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index c1bcf7bd..cdd93396 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -80,7 +80,7 @@ namespace chaiscript { } template - Ret call_func(Ret (*)(Params...), + Ret call_func_impl(Ret (*)(Params...), std::index_sequence, const Callable &f, [[maybe_unused]] const chaiscript::Function_Params ¶ms, @@ -95,14 +95,24 @@ namespace chaiscript { template Boxed_Value call_func(Ret (*sig)(Params...), const Callable &f, const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_conversions) { - if constexpr (std::is_same_v) { - call_func(sig, std::index_sequence_for{}, f, params, t_conversions); - return Handle_Return::handle(); - } else { - return Handle_Return::handle(call_func(sig, std::index_sequence_for{}, f, params, t_conversions)); - } + return Handle_Return::handle(call_func_impl(sig, std::index_sequence_for{}, f, params, t_conversions)); } +// MSVC has a broken warning for unreachable code in this block +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) +#endif + template + Boxed_Value + call_func(void (*sig)(Params...), const Callable &f, const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_conversions) { + call_func_impl(sig, std::index_sequence_for{}, f, params, t_conversions); + return Handle_Return::handle(); + } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + } // namespace detail } // namespace dispatch diff --git a/include/chaiscript/dispatchkit/short_alloc.hpp b/include/chaiscript/dispatchkit/short_alloc.hpp deleted file mode 100644 index 64f57446..00000000 --- a/include/chaiscript/dispatchkit/short_alloc.hpp +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef SHORT_ALLOC_H -#define SHORT_ALLOC_H - -// The MIT License (MIT) -// -// Copyright (c) 2015 Howard Hinnant -// -// 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 -#include - -template -class arena { - alignas(alignment) char buf_[N]; - char *ptr_; - -public: - ~arena() { ptr_ = nullptr; } - arena() noexcept - : ptr_(buf_) { - } - arena(const arena &) = delete; - arena &operator=(const arena &) = delete; - - template - char *allocate(std::size_t n); - void deallocate(char *p, std::size_t n) noexcept; - - static constexpr std::size_t size() noexcept { return N; } - std::size_t used() const noexcept { return static_cast(ptr_ - buf_); } - void reset() noexcept { ptr_ = buf_; } - -private: - static std::size_t align_up(std::size_t n) noexcept { return (n + (alignment - 1)) & ~(alignment - 1); } - - bool pointer_in_buffer(char *p) noexcept { return buf_ <= p && p <= buf_ + N; } -}; - -template -template -char *arena::allocate(std::size_t n) { - static_assert(ReqAlign <= alignment, "alignment is too small for this arena"); - assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); - auto const aligned_n = align_up(n); - if (static_cast(buf_ + N - ptr_) >= aligned_n) { - char *r = ptr_; - ptr_ += aligned_n; - return r; - } - - static_assert(alignment <= alignof(std::max_align_t), - "you've chosen an " - "alignment that is larger than alignof(std::max_align_t), and " - "cannot be guaranteed by normal operator new"); - return static_cast(::operator new(n)); -} - -template -void arena::deallocate(char *p, std::size_t n) noexcept { - assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); - if (pointer_in_buffer(p)) { - n = align_up(n); - if (p + n == ptr_) { - ptr_ = p; - } - } else { - ::operator delete(p); - } -} - -template -class short_alloc { -public: - using value_type = T; - static auto constexpr alignment = Align; - static auto constexpr size = N; - using arena_type = arena; - -private: - arena_type &a_; - -public: - short_alloc(const short_alloc &) = default; - short_alloc &operator=(const short_alloc &) = delete; - - explicit short_alloc(arena_type &a) noexcept - : a_(a) { - static_assert(size % alignment == 0, "size N needs to be a multiple of alignment Align"); - } - template - explicit short_alloc(const short_alloc &a) noexcept - : a_(a.a_) { - } - - template - struct rebind { - using other = short_alloc<_Up, N, alignment>; - }; - - T *allocate(std::size_t n) { return reinterpret_cast(a_.template allocate(n * sizeof(T))); } - void deallocate(T *p, std::size_t n) noexcept { a_.deallocate(reinterpret_cast(p), n * sizeof(T)); } - - template - friend bool operator==(const short_alloc &x, const short_alloc &y) noexcept; - - template - friend class short_alloc; -}; - -template -inline bool operator==(const short_alloc &x, const short_alloc &y) noexcept { - return N == M && A1 == A2 && &x.a_ == &y.a_; -} - -template -inline bool operator!=(const short_alloc &x, const short_alloc &y) noexcept { - return !(x == y); -} - -#endif // SHORT_ALLOC_HPP diff --git a/include/chaiscript/language/chaiscript_algebraic.hpp b/include/chaiscript/language/chaiscript_algebraic.hpp index 08592802..49cbf755 100644 --- a/include/chaiscript/language/chaiscript_algebraic.hpp +++ b/include/chaiscript/language/chaiscript_algebraic.hpp @@ -12,6 +12,7 @@ #include "../utility/hash.hpp" +#include #include namespace chaiscript { @@ -52,10 +53,10 @@ namespace chaiscript { invalid }; - constexpr static const char *to_string(Opers t_oper) noexcept { - constexpr const char *opers[] + constexpr static std::string_view to_string(Opers t_oper) noexcept { + constexpr const std::array opers = {"", "==", "<", ">", "<=", ">=", "!=", "", "=", "++", "--", "*=", "+=", "/=", "-=", "", "&=", "|=", "<<=", ">>=", "%=", "^=", "", "<<", ">>", "%", "&", "|", "^", "~", "", "+", "/", "*", "-", "+", "-", ""}; - return opers[static_cast(t_oper)]; + return opers[static_cast(t_oper)]; } constexpr static Opers to_operator(std::string_view t_str, bool t_is_unary = false) noexcept { diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 0ddd6f15..0d80b8ca 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -130,9 +130,9 @@ namespace chaiscript { namespace { /// Helper lookup to get the name of each node type constexpr const char *ast_node_type_to_string(AST_Node_Type ast_node_type) noexcept { - constexpr const char *const ast_node_types[] = {"Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled", "Const_Var_Decl", "Const_Assign_Decl", "Using", "Enum", "Namespace_Block"}; + constexpr std::array ast_node_types = {"Id", "Fun_Call", "Unused_Return_Fun_Call", "Arg_List", "Equation", "Var_Decl", "Assign_Decl", "Array_Call", "Dot_Access", "Lambda", "Block", "Scopeless_Block", "Def", "While", "If", "For", "Ranged_For", "Inline_Array", "Inline_Map", "Return", "File", "Prefix", "Break", "Continue", "Map_Pair", "Value_Range", "Inline_Range", "Try", "Catch", "Finally", "Method", "Attr_Decl", "Logical_And", "Logical_Or", "Reference", "Switch", "Case", "Default", "Noop", "Class", "Binary", "Arg", "Global_Decl", "Constant", "Compiled", "Const_Var_Decl", "Const_Assign_Decl", "Using", "Enum", "Namespace_Block"}; - return ast_node_types[static_cast(ast_node_type)]; + return ast_node_types[static_cast(ast_node_type)]; } } // namespace diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index d75535b8..fa46a460 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -122,7 +122,7 @@ namespace chaiscript { chaiscript::detail::Dispatch_Engine &get_eval_engine() noexcept { return m_engine; } /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude. - void build_eval_system(const ModulePtr &t_lib, const std::vector &t_opts, const bool t_no_io = false) { + void build_eval_system(const ModulePtr &t_lib, const std::vector &t_opts, const bool t_no_io) { if (t_lib) { add(t_lib); } @@ -317,7 +317,7 @@ namespace chaiscript { std::vector t_module_paths = {}, std::vector t_use_paths = {}, const std::vector &t_opts = chaiscript::default_options(), - const bool t_no_io = false) + const bool t_no_io=false) : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))) , m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))) , m_parser(std::move(parser)) @@ -611,7 +611,7 @@ namespace chaiscript { /// (the symbol mentioned above), an exception is thrown. /// /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found. - std::string load_module(const std::string &t_module_name) { + std::string load_module([[maybe_unused]] const std::string &t_module_name) { #ifdef CHAISCRIPT_NO_DYNLOAD throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); #else @@ -777,7 +777,7 @@ namespace chaiscript { throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); } - m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace & { + m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable noexcept -> Namespace & { t_namespace_generator(space); return space; })); @@ -786,7 +786,7 @@ namespace chaiscript { while (pos != std::string::npos) { const std::string parent = t_namespace_name.substr(0, pos); if (!m_namespace_generators.count(parent)) { - m_namespace_generators.emplace(std::make_pair(parent, [space = Namespace()]() mutable -> Namespace & { + m_namespace_generators.emplace(std::make_pair(parent, [space = Namespace()]() mutable noexcept -> Namespace & { return space; })); } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 2b7b8320..635aac3f 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1246,7 +1246,7 @@ namespace chaiscript { const auto &ns_name = this->children[0]->text; auto ns_name_bv = const_var(ns_name); - t_ss->call_function("namespace", m_ns_loc, Function_Params{ns_name_bv}, t_ss.conversions()); + t_ss->call_function("namespace", m_ns_loc, Function_Params{&ns_name_bv, 1}, t_ss.conversions()); std::vector parts; { @@ -1350,7 +1350,7 @@ namespace chaiscript { }; const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) { - return dispatch::dispatch(*t_funcs, Function_Params{t_param}, t_ss.conversions()); + return dispatch::dispatch(*t_funcs, Function_Params{&t_param, 1}, t_ss.conversions()); }; const std::string &loop_var_name = this->children[0]->text; @@ -1646,8 +1646,8 @@ namespace chaiscript { return Boxed_Number::do_oper(m_oper, bv); } else { chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params(Function_Params{bv}); - return t_ss->call_function(this->text, m_loc, Function_Params{bv}, t_ss.conversions()); + fpp.save_params(Function_Params{&bv, 1}); + return t_ss->call_function(this->text, m_loc, Function_Params{&bv, 1}, t_ss.conversions()); } } catch (const exception::dispatch_error &e) { throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss); @@ -1755,7 +1755,7 @@ namespace chaiscript { if (dispatch::Param_Types( std::vector>{Arg_List_AST_Node::get_arg_type(*catch_block.children[0], t_ss)}) - .match(Function_Params{t_except}, t_ss.conversions()) + .match(Function_Params{&t_except, 1}, t_ss.conversions()) .first) { t_ss.add_object(name, t_except); diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 3ad3a7a2..715f6ffe 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -22,15 +23,11 @@ #include "../dispatchkit/boxed_value.hpp" #include "../utility/hash.hpp" #include "../utility/static_string.hpp" +#include "../utility/unicode.hpp" #include "chaiscript_common.hpp" #include "chaiscript_optimizer.hpp" #include "chaiscript_tracer.hpp" -#if defined(CHAISCRIPT_UTF16_UTF32) -#include -#include -#endif - #if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) #define CHAISCRIPT_PUSHED_MIN_MAX #pragma push_macro("max") // Why Microsoft? why? This is worse than bad @@ -64,41 +61,26 @@ namespace chaiscript { // Generic for u16, u32 and wchar template struct Char_Parser_Helper { - // common for all implementations - static std::string u8str_from_ll(long long val) { - using char_type = std::string::value_type; - - char_type c[2]; - c[1] = char_type(val); - c[0] = char_type(val >> 8); - - if (c[0] == 0) { - return std::string(1, c[1]); // size, character - } - - return std::string(c, 2); // char buffer, size - } - static string_type str_from_ll(long long val) { - using target_char_type = typename string_type::value_type; -#if defined(CHAISCRIPT_UTF16_UTF32) - // prepare converter - std::wstring_convert, target_char_type> converter; - // convert - return converter.from_bytes(u8str_from_ll(val)); -#else - // no conversion available, just put value as character - return string_type(1, target_char_type(val)); // size, character -#endif + string_type out; + utility::unicode::append_codepoint(out, static_cast(val)); + return out; } }; - // Specialization for char AKA UTF-8 + // Specialization for char AKA UTF-8: preserve raw two-byte packing + // of multi-character literals. template<> struct Char_Parser_Helper { static std::string str_from_ll(long long val) { - // little SFINAE trick to avoid base class - return Char_Parser_Helper::u8str_from_ll(val); + using char_type = std::string::value_type; + char_type c[2]; + c[1] = char_type(val); + c[0] = char_type(val >> 8); + if (c[0] == 0) { + return std::string(1, c[1]); + } + return std::string(c, 2); } }; } // namespace detail @@ -128,9 +110,7 @@ namespace chaiscript { template constexpr static void set_alphabet(Array2D &array, const First first, const Second second) noexcept { - auto *first_ptr = &std::get<0>(array) + static_cast(first); - auto *second_ptr = &std::get<0>(*first_ptr) + static_cast(second); - *second_ptr = true; + array[static_cast(first)][static_cast(second)] = true; } constexpr static std::array, detail::max_alphabet> build_alphabet() noexcept { @@ -360,13 +340,13 @@ namespace chaiscript { ++col; } - ++m_pos; + std::advance(m_pos, 1); } return *this; } constexpr Position &operator--() noexcept { - --m_pos; + std::advance(m_pos, -1); if (*m_pos == '\n') { --line; col = m_last_col; @@ -377,7 +357,7 @@ namespace chaiscript { } constexpr Position &operator+=(size_t t_distance) noexcept { - *this = (*this) + t_distance; + *this = *this + t_distance; return *this; } @@ -406,9 +386,9 @@ namespace chaiscript { constexpr bool operator!=(const Position &t_rhs) const noexcept { return m_pos != t_rhs.m_pos; } - constexpr bool has_more() const noexcept { return m_pos != m_end; } + [[nodiscard]] constexpr bool has_more() const noexcept { return m_pos != m_end; } - constexpr size_t remaining() const noexcept { return static_cast(m_end - m_pos); } + [[nodiscard]] constexpr size_t remaining() const noexcept { return static_cast(m_end - m_pos); } constexpr const char &operator*() const noexcept { if (m_pos == m_end) { @@ -458,7 +438,7 @@ namespace chaiscript { constexpr static Operator_Matches m_operator_matches{}; /// test a char in an m_alphabet - constexpr bool char_in_alphabet(char c, detail::Alphabet a) const noexcept { return m_alphabet[a][static_cast(c)]; } + [[nodiscard]] constexpr bool char_in_alphabet(char c, detail::Alphabet a) const noexcept { return m_alphabet[a][static_cast(c)]; } /// Prints the parsed ast_nodes as a tree void debug_print(const AST_Node &t, std::string prepend = "") const override { @@ -509,7 +489,7 @@ namespace chaiscript { if (m_position.remaining() >= len) { const char *file_pos = &(*m_position); for (size_t pos = 0; pos < len; ++pos) { - if (sym.c_str()[pos] != file_pos[pos]) { + if (sym[pos] != *std::next(file_pos, static_cast(pos))) { return false; } } @@ -530,20 +510,9 @@ namespace chaiscript { } } return true; - } else if (Symbol_(m_singleline_comment)) { - while (m_position.has_more()) { - if (Symbol_(m_cr_lf)) { - m_position -= 2; - break; - } else if (Char_('\n')) { - --m_position; - break; - } else { - ++m_position; - } - } - return true; - } else if (Symbol_(m_annotation)) { + } + + if (Symbol_(m_singleline_comment) || Symbol_(m_annotation)) { while (m_position.has_more()) { if (Symbol_(m_cr_lf)) { m_position -= 2; @@ -1060,7 +1029,7 @@ namespace chaiscript { template struct Char_Parser { string_type &match; - using char_type = typename string_type::value_type; + using char_type = string_type::value_type; bool is_escaped = false; bool is_interpolated = false; bool saw_interpolation_marker = false; @@ -1119,40 +1088,20 @@ namespace chaiscript { } void process_unicode() { - const auto ch = static_cast(std::stoi(hex_matches, nullptr, 16)); + const auto ch = static_cast(std::stoi(hex_matches, nullptr, 16)); const auto match_size = hex_matches.size(); hex_matches.clear(); is_escaped = false; const auto u_size = unicode_size; unicode_size = 0; - char buf[4]; if (u_size != match_size) { throw exception::eval_error("Incomplete unicode escape sequence"); } - if (u_size == 4 && ch >= 0xD800 && ch <= 0xDFFF) { + if (u_size == 4 && utility::unicode::is_surrogate(ch)) { throw exception::eval_error("Invalid 16 bit universal character"); } - - if (ch < 0x80) { - match += static_cast(ch); - } else if (ch < 0x800) { - buf[0] = static_cast(0xC0 | (ch >> 6)); - buf[1] = static_cast(0x80 | (ch & 0x3F)); - match.append(buf, 2); - } else if (ch < 0x10000) { - buf[0] = static_cast(0xE0 | (ch >> 12)); - buf[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - buf[2] = static_cast(0x80 | (ch & 0x3F)); - match.append(buf, 3); - } else if (ch < 0x200000) { - buf[0] = static_cast(0xF0 | (ch >> 18)); - buf[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); - buf[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); - buf[3] = static_cast(0x80 | (ch & 0x3F)); - match.append(buf, 4); - } else { - // this must be an invalid escape sequence? + if (utility::unicode::append_utf8(match, ch) == 0) { throw exception::eval_error("Invalid 32 bit universal character"); } } @@ -1548,7 +1497,7 @@ namespace chaiscript { if (m_position.remaining() >= len) { auto tmp = m_position; for (size_t i = 0; tmp.has_more() && i < len; ++i) { - if (*tmp != t_s.c_str()[i]) { + if (*tmp != t_s[i]) { return false; } ++tmp; @@ -1575,7 +1524,7 @@ namespace chaiscript { return retval; } - bool is_operator(std::string_view t_s) const noexcept { return m_operator_matches.is_match(t_s); } + [[nodiscard]] bool is_operator(std::string_view t_s) const noexcept { return m_operator_matches.is_match(t_s); } /// Reads (and potentially captures) a symbol group from input if it matches the parameter bool Symbol(const utility::Static_String &t_s, const bool t_disallow_prevention = false) { @@ -2749,7 +2698,7 @@ namespace chaiscript { bool retval = false; const auto prev_stack_top = m_match_stack.size(); - if (m_operators[t_precedence] != Operator_Precedence::Prefix) { + if (t_precedence < m_operators.size() && m_operators[t_precedence] < Operator_Precedence::Prefix) { if (Operator(t_precedence + 1)) { retval = true; std::string oper; @@ -2763,7 +2712,7 @@ namespace chaiscript { } switch (m_operators[t_precedence]) { - case (Operator_Precedence::Ternary_Cond): + case Operator_Precedence::Ternary_Cond: if (Symbol(":")) { if (!Operator(t_precedence + 1)) { throw exception::eval_error("Incomplete '" + oper + "' expression", @@ -2778,24 +2727,24 @@ namespace chaiscript { } break; - case (Operator_Precedence::Addition): - case (Operator_Precedence::Multiplication): - case (Operator_Precedence::Shift): - case (Operator_Precedence::Equality): - case (Operator_Precedence::Bitwise_And): - case (Operator_Precedence::Bitwise_Xor): - case (Operator_Precedence::Bitwise_Or): - case (Operator_Precedence::Comparison): + case Operator_Precedence::Addition: + case Operator_Precedence::Multiplication: + case Operator_Precedence::Shift: + case Operator_Precedence::Equality: + case Operator_Precedence::Bitwise_And: + case Operator_Precedence::Bitwise_Xor: + case Operator_Precedence::Bitwise_Or: + case Operator_Precedence::Comparison: build_match>(prev_stack_top, oper); break; - case (Operator_Precedence::Logical_And): + case Operator_Precedence::Logical_And: build_match>(prev_stack_top, oper); break; - case (Operator_Precedence::Logical_Or): + case Operator_Precedence::Logical_Or: build_match>(prev_stack_top, oper); break; - case (Operator_Precedence::Prefix): + case Operator_Precedence::Prefix: assert(false); // cannot reach here because of if() statement at the top break; @@ -2992,7 +2941,7 @@ namespace chaiscript { /// Parses the given input string, tagging parsed ast_nodes with the given m_filename. AST_NodePtr parse_internal(const std::string &t_input, std::string t_fname) { const auto begin = t_input.empty() ? nullptr : &t_input.front(); - const auto end = begin == nullptr ? nullptr : begin + t_input.size(); + const auto end = begin == nullptr ? nullptr : std::next(begin, std::ssize(t_input)); m_position = Position(begin, end); m_filename = std::make_shared(std::move(t_fname)); diff --git a/include/chaiscript/utility/hash.hpp b/include/chaiscript/utility/hash.hpp index 15bc4d9c..9a5b49f5 100644 --- a/include/chaiscript/utility/hash.hpp +++ b/include/chaiscript/utility/hash.hpp @@ -8,6 +8,8 @@ #define CHAISCRIPT_UTILITY_FNV1A_HPP_ #include "../chaiscript_defines.hpp" + +#include #include namespace chaiscript { @@ -28,7 +30,7 @@ namespace chaiscript { while (begin != end) { h = (h ^ (*begin)) * 0x01000193; - ++begin; + std::advance(begin, 1); } return h; @@ -43,7 +45,7 @@ namespace chaiscript { template static constexpr std::uint32_t hash(const char (&str)[N]) noexcept { - return hash(std::begin(str), std::end(str) - 1); + return hash(std::begin(str), std::prev(std::end(str))); } static constexpr std::uint32_t hash(std::string_view sv) noexcept { @@ -64,7 +66,7 @@ namespace chaiscript { hash += std::uint32_t(*begin); hash += hash << 10; hash ^= hash >> 6; - ++begin; + std::advance(begin, 1); } hash += hash << 3; diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index aa8fe698..a7f998cc 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -7,6 +7,7 @@ #include "../chaiscript_defines.hpp" #include "quick_flat_map.hpp" +#include "unicode.hpp" #include #include #include @@ -44,21 +45,17 @@ namespace chaiscript::json { = std::variant, std::vector, std::string, double, std::int64_t, bool>; struct Internal { - Internal(std::nullptr_t) - : d(nullptr) { - } - Internal() - : d(nullptr) { - } - Internal(Class c) + explicit Internal(std::nullptr_t) {} + Internal() = default; + Internal(const Class c) : d(make_type(c)) { } template - Internal(T t) + explicit Internal(T t) : d(std::move(t)) { } - static Data make_type(Class c) { + static Data make_type(const Class c) { switch (c) { case Class::Null: return nullptr; @@ -84,7 +81,7 @@ namespace chaiscript::json { } } - Class type() const noexcept { return Class(d.index()); } + [[nodiscard]] Class type() const noexcept { return Class(d.index()); } template decltype(auto) visit_or(Visitor &&visitor, Or &&other) const { @@ -108,14 +105,14 @@ namespace chaiscript::json { auto &Float() { return get_set_type(); } auto &Bool() { return get_set_type(); } - auto Map() const noexcept { return std::get_if(Class::Object)>(&d); } - auto Vector() const noexcept { return std::get_if(Class::Array)>(&d); } - auto String() const noexcept { return std::get_if(Class::String)>(&d); } - auto Int() const noexcept { return std::get_if(Class::Integral)>(&d); } - auto Float() const noexcept { return std::get_if(Class::Floating)>(&d); } - auto Bool() const noexcept { return std::get_if(Class::Boolean)>(&d); } + [[nodiscard]] auto Map() const noexcept { return std::get_if(Class::Object)>(&d); } + [[nodiscard]] auto Vector() const noexcept { return std::get_if(Class::Array)>(&d); } + [[nodiscard]] auto String() const noexcept { return std::get_if(Class::String)>(&d); } + [[nodiscard]] auto Int() const noexcept { return std::get_if(Class::Integral)>(&d); } + [[nodiscard]] auto Float() const noexcept { return std::get_if(Class::Floating)>(&d); } + [[nodiscard]] auto Bool() const noexcept { return std::get_if(Class::Boolean)>(&d); } - Data d; + Data d{nullptr}; }; Internal internal; @@ -129,12 +126,12 @@ namespace chaiscript::json { JSONWrapper(Container *val) : object(val) { } - JSONWrapper(std::nullptr_t) {} + JSONWrapper(std::nullptr_t) { } - typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } - typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); } - typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } - typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } + Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); } + Container::iterator end() { return object ? object->end() : typename Container::iterator(); } + [[nodiscard]] Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); } + [[nodiscard]] Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); } }; template @@ -147,10 +144,10 @@ namespace chaiscript::json { } JSONConstWrapper(std::nullptr_t) {} - typename Container::const_iterator begin() const noexcept { + [[nodiscard]] Container::const_iterator begin() const noexcept { return object ? object->begin() : typename Container::const_iterator(); } - typename Container::const_iterator end() const noexcept { return object ? object->end() : typename Container::const_iterator(); } + [[nodiscard]] Container::const_iterator end() const noexcept { return object ? object->end() : typename Container::const_iterator(); } }; JSON() = default; @@ -162,7 +159,9 @@ namespace chaiscript::json { JSON(initializer_list list) : internal(Class::Object) { - for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i) { + for (auto i = list.begin(), e = list.end(); + i != e; + std::advance(i, 2)) { operator[](i->to_string()) = *std::next(i); } } @@ -471,22 +470,8 @@ namespace chaiscript::json { } } offset += 4; - const auto ch = static_cast(std::stoi(hex_matches, nullptr, 16)); - if (ch < 0x80) { - val += static_cast(ch); - } else if (ch < 0x800) { - val += static_cast(0xC0 | (ch >> 6)); - val += static_cast(0x80 | (ch & 0x3F)); - } else if (ch < 0x10000) { - val += static_cast(0xE0 | (ch >> 12)); - val += static_cast(0x80 | ((ch >> 6) & 0x3F)); - val += static_cast(0x80 | (ch & 0x3F)); - } else if (ch < 0x200000) { - val += static_cast(0xF0 | (ch >> 18)); - val += static_cast(0x80 | ((ch >> 12) & 0x3F)); - val += static_cast(0x80 | ((ch >> 6) & 0x3F)); - val += static_cast(0x80 | (ch & 0x3F)); - } else { + const auto ch = static_cast(std::stoi(hex_matches, nullptr, 16)); + if (chaiscript::utility::unicode::append_utf8(val, ch) == 0) { throw std::runtime_error(std::string("JSON ERROR: String: Invalid 32 bit universal character")); } } break; diff --git a/include/chaiscript/utility/static_string.hpp b/include/chaiscript/utility/static_string.hpp index 8613fd31..39ceb5f5 100644 --- a/include/chaiscript/utility/static_string.hpp +++ b/include/chaiscript/utility/static_string.hpp @@ -7,6 +7,8 @@ #ifndef CHAISCRIPT_UTILITY_STATIC_STRING_HPP_ #define CHAISCRIPT_UTILITY_STATIC_STRING_HPP_ +#include + namespace chaiscript::utility { struct Static_String { template @@ -21,30 +23,17 @@ namespace chaiscript::utility { constexpr auto begin() const noexcept { return data; } - constexpr auto end() const noexcept { return data + m_size; } + constexpr auto end() const noexcept { return std::next(data, static_cast(m_size)); } - constexpr bool operator==(std::string_view other) const noexcept { - // return std::string_view(data, m_size) == other; - auto b1 = begin(); - const auto e1 = end(); - auto b2 = other.begin(); - const auto e2 = other.end(); - - if (e1 - b1 != e2 - b2) { - return false; - } - - while (b1 != e1) { - if (*b1 != *b2) { - return false; - } - ++b1; - ++b2; - } - return true; + constexpr auto operator[](const std::size_t idx) const noexcept { + return *std::next(data, static_cast(idx)); } - bool operator==(const std::string &t_str) const noexcept { return std::equal(begin(), end(), std::cbegin(t_str), std::cend(t_str)); } + constexpr bool operator==(std::string_view other) const noexcept { + return std::string_view(data, m_size) == other; + } + + constexpr bool operator==(const std::string &t_str) const noexcept { return std::equal(begin(), end(), std::cbegin(t_str), std::cend(t_str)); } const size_t m_size; const char *data = nullptr; diff --git a/include/chaiscript/utility/unicode.hpp b/include/chaiscript/utility/unicode.hpp new file mode 100644 index 00000000..294278ad --- /dev/null +++ b/include/chaiscript/utility/unicode.hpp @@ -0,0 +1,91 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_UTILITY_UNICODE_HPP_ +#define CHAISCRIPT_UTILITY_UNICODE_HPP_ + +#include +#include +#include + +namespace chaiscript { + namespace utility { + namespace unicode { + + inline constexpr std::uint32_t max_codepoint = 0x10FFFF; + + constexpr bool is_surrogate(std::uint32_t cp) noexcept { return cp >= 0xD800 && cp <= 0xDFFF; } + + // Append cp to out as UTF-8. Returns bytes written, or 0 if cp >= 0x200000. + // Surrogates are not rejected here; callers that care check is_surrogate() first. + inline std::size_t append_utf8(std::string &out, std::uint32_t cp) { + if (cp < 0x80) { + out += static_cast(cp); + return 1; + } + if (cp < 0x800) { + out += static_cast(0xC0 | (cp >> 6)); + out += static_cast(0x80 | (cp & 0x3F)); + return 2; + } + if (cp < 0x10000) { + out += static_cast(0xE0 | (cp >> 12)); + out += static_cast(0x80 | ((cp >> 6) & 0x3F)); + out += static_cast(0x80 | (cp & 0x3F)); + return 3; + } + if (cp < 0x200000) { + out += static_cast(0xF0 | (cp >> 18)); + out += static_cast(0x80 | ((cp >> 12) & 0x3F)); + out += static_cast(0x80 | ((cp >> 6) & 0x3F)); + out += static_cast(0x80 | (cp & 0x3F)); + return 4; + } + return 0; + } + + // Append cp to out as UTF-16. Returns code units written, or 0 if cp is + // a surrogate or > max_codepoint. + template + inline std::size_t append_utf16(std::basic_string &out, std::uint32_t cp) { + if (is_surrogate(cp) || cp > max_codepoint) { + return 0; + } + if (cp < 0x10000) { + out += static_cast(cp); + return 1; + } + const std::uint32_t v = cp - 0x10000; + out += static_cast(0xD800 | (v >> 10)); + out += static_cast(0xDC00 | (v & 0x3FF)); + return 2; + } + + // Append cp to a basic_string. Dispatches on sizeof(CharT): + // 1 byte -> UTF-8, 2 bytes -> UTF-16, 4 bytes -> UTF-32. + // Returns code units written, or 0 if the codepoint is invalid. + template + inline std::size_t append_codepoint(std::basic_string &out, std::uint32_t cp) { + if constexpr (sizeof(CharT) == 1) { + std::string tmp; + const auto n = append_utf8(tmp, cp); + out.append(tmp.begin(), tmp.end()); + return n; + } else if constexpr (sizeof(CharT) == 2) { + return append_utf16(out, cp); + } else { + static_assert(sizeof(CharT) == 4, "append_codepoint: unsupported CharT size"); + if (is_surrogate(cp) || cp > max_codepoint) { + return 0; + } + out += static_cast(cp); + return 1; + } + } + + } // namespace unicode + } // namespace utility +} // namespace chaiscript + +#endif diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index da5417ce..0a992679 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -30,9 +30,8 @@ char *mystrdup(const char *s) { #ifdef CHAISCRIPT_MSVC strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d, s, len); // Copy the characters + strncpy(d, s, len + 1); // Copy the characters #endif - d[len] = '\0'; return d; // Return the new string } @@ -297,7 +296,7 @@ int main(int argc, char *argv[]) { ++i; } - std::string arg(i ? argv[i] : "--interactive"); + std::string arg(i ? *std::next(argv, i) : "--interactive"); enum { eInteractive, @@ -311,7 +310,7 @@ int main(int argc, char *argv[]) { std::cout << "insufficient input following " << arg << std::endl; return EXIT_FAILURE; } else { - arg = argv[++i]; + arg = *std::next(argv, ++i); } } else if (arg == "-" || arg == "--stdin") { arg = ""; diff --git a/src/libfuzzer_client.cpp b/src/libfuzzer_client.cpp index 39ebe2f4..25726611 100644 --- a/src/libfuzzer_client.cpp +++ b/src/libfuzzer_client.cpp @@ -35,9 +35,8 @@ char *mystrdup(const char *s) { #ifdef CHAISCRIPT_MSVC strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d, s, len); // Copy the characters + strncpy(d, s, len + 1); // Copy the characters #endif - d[len] = '\0'; return d; // Return the new string } diff --git a/src/main.cpp b/src/main.cpp index ab94b5d1..52db4b1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,6 +8,7 @@ // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include +#include #include #include #include @@ -34,9 +35,8 @@ char *mystrdup(const char *s) { #ifdef CHAISCRIPT_MSVC strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d, s, len); // Copy the characters + strncpy(d, s, len + 1); // Copy the characters #endif - d[len] = '\0'; return d; // Return the new string } @@ -283,7 +283,7 @@ int main(int argc, char *argv[]) { ++i; } - std::string arg(i != 0 ? argv[i] : "--interactive"); + std::string arg(i != 0 ? *std::next(argv, i) : "--interactive"); enum { eInteractive, @@ -297,7 +297,7 @@ int main(int argc, char *argv[]) { std::cout << "insufficient input following " << arg << '\n'; return EXIT_FAILURE; } - arg = argv[++i]; + arg = *std::next(argv, ++i); } else if (arg == "-" || arg == "--stdin") { arg = ""; diff --git a/unittests/catch.hpp b/unittests/catch.hpp deleted file mode 100644 index 07efa655..00000000 --- a/unittests/catch.hpp +++ /dev/null @@ -1,17969 +0,0 @@ -/* - * Catch v2.13.9 - * Generated: 2022-04-12 22:37:23.260201 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 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 -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 9 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -# if !defined(__clang__) // Handle Clang masquerading for msvc - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL - -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#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 ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template