diff --git a/.clang-format b/.clang-format index d0c095e1..8c8b3b26 100644 --- a/.clang-format +++ b/.clang-format @@ -1,98 +1,33 @@ -AccessModifierOffset: -2 -AlignAfterOpenBracket: DontAlign -AlignConsecutiveAssignments: false -AlignConsecutiveDeclarations: false -AlignEscapedNewlines: Left -AlignOperands: true -AlignTrailingComments: false -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: true -AllowShortCaseLabelsOnASingleLine: false -AllowShortFunctionsOnASingleLine: All -AllowShortIfStatementsOnASingleLine: true -AllowShortLoopsOnASingleLine: true -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: true -AlwaysBreakTemplateDeclarations: false -BinPackArguments: false -BinPackParameters: false -BraceWrapping: - AfterClass: true - AfterControlStatement: false - AfterEnum: false - AfterFunction: true - AfterNamespace: false - AfterObjCDeclaration: false - AfterStruct: true - AfterUnion: false - BeforeCatch: false - BeforeElse: false - IndentBraces: false - SplitEmptyFunction: false - SplitEmptyNamespace: true - SplitEmptyRecord: true -BreakAfterJavaFieldAnnotations: true -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeBraces: Custom -BreakBeforeInheritanceComma: true -BreakBeforeTernaryOperators: true -BreakConstructorInitializers: BeforeColon -BreakConstructorInitializersBeforeComma: false -BreakStringLiterals: true -ColumnLimit: 0 -CommentPragmas: '^ IWYU pragma:' -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 2 -ContinuationIndentWidth: 2 -Cpp11BracedListStyle: false -DerivePointerAlignment: true -DisableFormat: false -ExperimentalAutoDetectBinPacking: true -FixNamespaceComments: true -ForEachMacros: -- foreach -- Q_FOREACH -- BOOST_FOREACH -IncludeCategories: -- Priority: 2 - Regex: ^"(llvm|llvm-c|clang|clang-c)/ -- Priority: 3 - Regex: ^(<|"(gtest|gmock|isl|json)/) -- Priority: 1 - Regex: .* -IncludeIsMainRegex: (Test)?$ -IndentCaseLabels: false -IndentWidth: 2 -IndentWrappedFunctionNames: true -JavaScriptQuotes: Leave -JavaScriptWrapImports: true -KeepEmptyLinesAtTheStartOfBlocks: true -Language: Cpp -MacroBlockBegin: '' -MacroBlockEnd: '' -MaxEmptyLinesToKeep: 2 -NamespaceIndentation: Inner -ObjCBlockIndentWidth: 7 -ObjCSpaceAfterProperty: true -ObjCSpaceBeforeProtocolList: false -PointerAlignment: Right -ReflowComments: true -SortIncludes: false -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterTemplateKeyword: false -SpaceBeforeAssignmentOperators: true -SpaceBeforeParens: ControlStatements -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 0 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: true -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: Cpp11 -TabWidth: 8 -UseTab: Never - +# clang-format: 11 +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveBitFields: false +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: Inline +AllowShortLambdasOnASingleLine: All +AlwaysBreakTemplateDeclarations: true +BasedOnStyle: WebKit +BinPackArguments: true +BinPackParameters: true +BreakBeforeBraces: Attach +ColumnLimit: 0 +Cpp11BracedListStyle: true +FixNamespaceComments: true +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: false +NamespaceIndentation: All +PenaltyBreakBeforeFirstCallParameter: 200 +PenaltyBreakComment: 5 +PenaltyBreakFirstLessLess: 50 +PenaltyExcessCharacter: 4 +PointerAlignment: Right +SortIncludes: true +SpaceAfterTemplateKeyword: false +SpaceBeforeCpp11BracedList: false +SpaceInEmptyBlock: false +Standard: Latest +TabWidth: 2 +UseTab: Never \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 7333e3b0..6a103736 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,12 @@ addons: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-5.0 + - sourceline: 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' packages: - - g++-4.9 - - g++-5 - - g++-6 + - g++-7 + - g++-8 coverity_scan: project: name: "ChaiScript/ChaiScript" @@ -22,7 +24,7 @@ matrix: include: - os: linux sudo: false - env: GCC_VER="4.9" + env: GCC_VER="7" compiler: gcc # - os: linux #sudo: false @@ -30,19 +32,20 @@ matrix: #compiler: gcc - os: linux sudo: false - env: GCC_VER="5" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + env: GCC_VER="7" CPPCHECK=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - os: linux sudo: false - env: GCC_VER="6" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" + env: GCC_VER="8" CPPCHECK=1 COVERAGE=1 CMAKE_OPTIONS="-D RUN_FUZZY_TESTS:BOOL=TRUE" compiler: gcc - - os: osx - compiler: clang - osx_image: xcode8 - - os: osx - compiler: clang - osx_image: xcode8 - env: CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 + #- os: osx + # compiler: clang + # osx_image: xcode10 + # env: CLANG_VER="5.0" + #- os: osx + # compiler: clang + # osx_image: xcode10 + # env: CLANG_VER="5.0" CMAKE_OPTIONS="-D DYNLOAD_ENABLED:BOOL=FALSE -D MULTITHREAD_SUPPORT_ENABLED:BOOL=FALSE -D USE_STD_MAKE_SHARED:BOOL=TRUE" BUILD_ONLY=1 env: global: @@ -52,6 +55,7 @@ env: before_install: - if [ "${GCC_VER}" != "" ]; then export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" ; fi + #- if [ "${CLANG_VER}" != "" ]; then export CXX="clang++-$CLANG_VER"; fi - pip install --user cpp-coveralls script: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab8f3b1..b706a15a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,11 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.12) +cmake_policy(SET CMP0054 NEW) -if(NOT ${CMAKE_VERSION} VERSION_LESS "3.1") - cmake_policy(SET CMP0054 NEW) -endif() +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# required since cmake 3.4 at least for libc++ +set(CMAKE_ENABLE_EXPORTS ON) project(chaiscript) @@ -15,7 +18,6 @@ option(BUILD_SAMPLES "Build Samples Folder" FALSE) option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE) option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE) option(RUN_PERFORMANCE_TESTS "Run Performance Tests" FALSE) -option(BUILD_IN_CPP17_MODE "Build with C++17 flags" FALSE) mark_as_advanced(USE_STD_MAKE_SHARED) @@ -49,8 +51,8 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") option(ENABLE_MEMORY_SANITIZER "Enable memory sanitizer testing in gcc/clang" FALSE) if(ENABLE_MEMORY_SANITIZER) - add_definitions(-fsanitize=memory -g) - set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=memory") + add_definitions(-fsanitize=memory -fsanitize-memory-track-origins -g) + set(LINKER_FLAGS "${LINKER_FLAGS} -fsanitize=memory -fsanitize-memory-track-origins ") endif() option(ENABLE_UNDEFINED_SANITIZER "Enable undefined behavior sanitizer testing in gcc/clang" FALSE) @@ -60,27 +62,26 @@ if(CMAKE_COMPILER_IS_GNUCC OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") endif() option(ENABLE_LTO "Enable Link Time Optimization" FALSE) - - if (ENABLE_LTO) - add_definitions(-flto) - set(LINKER_FLAGS "${LINKER_FLAGS} -flto") + if(ENABLE_LTO) + check_ipo_supported() + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() option(GPROF_OUTPUT "Generate profile data" FALSE) - if (GPROF_OUTPUT) + if(GPROF_OUTPUT) add_definitions(-pg) set(LINKER_FLAGS "${LINKER_FLAGS} -pg") endif() option(PROFILE_GENERATE "Generate profile data" FALSE) - if (PROFILE_GENERATE) + if(PROFILE_GENERATE) add_definitions(-fprofile-generate) set(LINKER_FLAGS "${LINKER_FLAGS} -fprofile-generate") endif() option(PROFILE_USE "Use profile data" FALSE) - if (PROFILE_USE) + if(PROFILE_USE) add_definitions(-fprofile-use) set(LINKER_FLAGS "${LINKER_FLAGS} -fprofile-use") endif() @@ -98,8 +99,8 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt") set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/readme.md") set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt") -set(CPACK_PACKAGE_VERSION_MAJOR 6) -set(CPACK_PACKAGE_VERSION_MINOR 1) +set(CPACK_PACKAGE_VERSION_MAJOR 7) +set(CPACK_PACKAGE_VERSION_MINOR 0) set(CPACK_PACKAGE_VERSION_PATCH 0) set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval") @@ -125,7 +126,7 @@ if(NOT MINGW) find_library(READLINE_LIBRARY NAMES readline PATH /usr/lib /usr/local/lib /opt/local/lib) endif() -if (UNIX AND NOT APPLE) +if(UNIX AND NOT APPLE) find_program(VALGRIND NAMES valgrind PATH /usr/bin /usr/local/bin) endif() @@ -143,36 +144,10 @@ else() set(READLINE_FLAG) endif() -if(CMAKE_COMPILER_IS_GNUCC) - execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) - - if(GCC_VERSION VERSION_LESS 4.9) - set(CPP14_FLAG "-std=c++1y") - else() - if (BUILD_IN_CPP17_MODE) - set(CPP14_FLAG "-std=c++1z") - else() - set(CPP14_FLAG "-std=c++14") - endif() - endif() -else() - if (BUILD_IN_CPP17_MODE) - set(CPP14_FLAG "-std=c++1z") - else() - set(CPP14_FLAG "-std=c++14") - endif() -endif() - if(MSVC) add_definitions(/W4 /w14545 /w34242 /w34254 /w34287 /w44263 /w44265 /w44296 /w44311 /w44826 /we4289 /w14546 /w14547 /w14549 /w14555 /w14619 /w14905 /w14906 /w14928) - if (BUILD_IN_CPP17_MODE) - add_definitions(/std:c++17) - endif() - - - - if (MSVC_VERSION STREQUAL "1800") + if(MSVC_VERSION STREQUAL "1800") # VS2013 doesn't have magic statics add_definitions(/w44640) else() @@ -180,7 +155,7 @@ if(MSVC) add_definitions(/w34062) endif() - add_definitions(/bigobj /permissive-) + add_definitions(/bigobj /permissive- /utf-8) # Note on MSVC compiler flags. # The code base selective disables warnings as necessary when the compiler is complaining too much # about something that is perfectly valid, or there is simply no technical way around it @@ -190,10 +165,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 -pedantic ${CPP14_FLAG}) + 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) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - 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) + 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) else() add_definitions(-Wnoexcept) endif() @@ -204,16 +179,12 @@ else() endif() if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - option(USE_LIBCXX "Use clang's libcxx" TRUE) + option(USE_LIBCXX "Use clang's libcxx" FALSE) if(USE_LIBCXX) add_definitions(-stdlib=libc++) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG} -stdlib=libc++") - else() - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") + set(LINKER_FLAGS "${LINKER_FLAGS} -stdlib=libc++") endif() -elseif(CMAKE_COMPILER_IS_GNUCC) - set(LINKER_FLAGS "${LINKER_FLAGS} ${CPP14_FLAG}") endif() # limitations in MinGW require us to make an optimized build @@ -279,8 +250,8 @@ add_executable(chai src/main.cpp ${Chai_INCLUDES}) target_link_libraries(chai ${LIBS} ${CHAISCRIPT_LIBS}) if(BUILD_SAMPLES) - add_executable(example samples/example.cpp) - target_link_libraries(example ${LIBS}) + add_executable(sanity_checks src/sanity_checks.cpp) + target_link_libraries(sanity_checks ${LIBS}) add_executable(test_num_exceptions samples/test_num_exceptions.cpp) target_link_libraries(test_num_exceptions ${LIBS} ${CHAISCRIPT_LIBS}) add_executable(memory_leak_test samples/memory_leak_test.cpp) @@ -312,7 +283,7 @@ file(GLOB PERFORMANCE_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/performance_tes list(SORT PERFORMANCE_TESTS) -if (RUN_FUZZY_TESTS) +if(RUN_FUZZY_TESTS) file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") @@ -341,37 +312,6 @@ endif() if(BUILD_TESTING) - # Add catch tests macro - macro(ADD_CATCH_TESTS executable) - if (MSVC) - file(TO_NATIVE_PATH "${QT_LIBRARY_DIR}" QT_LIB_PATH) - set(NEWPATH "${QT_LIB_PATH};$ENV{PATH}") - else() - set(NEWPATH $ENV{PATH}) - endif() - - get_target_property(target_files ${executable} SOURCES) - - foreach(source ${target_files}) - if(NOT "${source}" MATCHES "/moc_.*cxx") - string(REGEX MATCH .*cpp source "${source}") - if(source) - file(READ "${source}" contents) - string(REGEX MATCHALL "TEST_CASE\\([ ]*\"[^\"]+\"" found_tests ${contents}) - foreach(hit ${found_tests}) - string(REGEX REPLACE "TEST_CASE\\([ ]*(\"[^\"]+\").*" "\\1" test_name ${hit}) - add_test(compiled.${test_name} "${executable}" ${test_name}) - set_tests_properties(compiled.${test_name} PROPERTIES TIMEOUT 660 ENVIRONMENT "PATH=${NEWPATH}") - endforeach() - endif() - endif() - endforeach() - endmacro() - - - - - option(UNIT_TEST_LIGHT "Unit tests light (expect module loading failures)" FALSE) add_test(version_check chai -c "if(\"\\\${ version() };\\\${version_major()};\\\${version_minor()};\\\${version_patch()}\" != \"${CHAI_VERSION};${CPACK_PACKAGE_VERSION_MAJOR};${CPACK_PACKAGE_VERSION_MINOR};${CPACK_PACKAGE_VERSION_PATCH}\") { exit(-1) }") @@ -403,7 +343,7 @@ if(BUILD_TESTING) list(APPEND TESTS unit.${filename}) endforeach() - if (RUN_PERFORMANCE_TESTS) + if(RUN_PERFORMANCE_TESTS) foreach(filename ${PERFORMANCE_TESTS}) message(STATUS "Adding performance test ${filename}") @@ -460,10 +400,8 @@ if(BUILD_TESTING) "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" ) - endif() - add_executable(multifile_test unittests/multifile_test_main.cpp unittests/multifile_test_chai.cpp @@ -472,12 +410,11 @@ if(BUILD_TESTING) target_link_libraries(multifile_test ${LIBS}) add_test(NAME MultiFile_Test COMMAND multifile_test) - install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript) + install(TARGETS test_module RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/chaiscript") endif() endif() - if(BUILD_LIBFUZZ_TESTER) add_executable(fuzzer src/libfuzzer_client.cpp src/sha3.cpp) target_compile_options(fuzzer PRIVATE "-fsanitize=fuzzer,address") @@ -485,7 +422,7 @@ if(BUILD_LIBFUZZ_TESTER) endif() -install(TARGETS chai chaiscript_stdlib-${CHAI_VERSION} ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION lib/chaiscript) +install(TARGETS chai chaiscript_stdlib-${CHAI_VERSION} ${MODULES} RUNTIME DESTINATION bin LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/chaiscript") install(DIRECTORY include/chaiscript DESTINATION include PATTERN "*.hpp" @@ -506,6 +443,6 @@ install(DIRECTORY samples DESTINATION share/chaiscript configure_file(contrib/pkgconfig/chaiscript.pc.in lib/pkgconfig/chaiscript.pc @ONLY) install(FILES "${chaiscript_BINARY_DIR}/lib/pkgconfig/chaiscript.pc" - DESTINATION lib/pkgconfig) + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/LICENSE b/LICENSE index 8e6a6b98..8673d971 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ BSD-3-Clause License -Copyright 2009-2016 Jason Turner -Copyright 2009-2012 Jonathan Turner. +Copyright 2009-2018 Jason Turner All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/appveyor.yml b/appveyor.yml index 19187929..5a4fe67e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,10 +1,9 @@ version: 6.1.x.{build} -image: - - Visual Studio 2017 +image: + - Visual Studio 2019 environment: matrix: - - VS_VERSION: "Visual Studio 14" - - VS_VERSION: "Visual Studio 15" + - VS_VERSION: "Visual Studio 16" build_script: - cmd: >- mkdir build diff --git a/cheatsheet.md b/cheatsheet.md index 907fe329..50ee8aa4 100644 --- a/cheatsheet.md +++ b/cheatsheet.md @@ -11,20 +11,18 @@ ChaiScript tries to follow the [Semantic Versioning](http://semver.org/) scheme. # Initializing ChaiScript ``` -chaiscript::ChaiScript chai; // loads stdlib from loadable module on file system -chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); // compiles in stdlib +chaiscript::ChaiScript chai; // initializes ChaiScript, adding the standard ChaiScript types (map, string, ...) ``` Note that ChaiScript cannot be used as a global / static object unless it is being compiled with `CHAISCRIPT_NO_THREADS`. - # Adding Things To The Engine ## Adding a Function / Method / Member ### General -``` +```cpp chai.add(chaiscript::fun(&function_name), "function_name"); chai.add(chaiscript::fun(&Class::method_name), "method_name"); chai.add(chaiscript::fun(&Class::member_name), "member_name"); @@ -32,7 +30,7 @@ chai.add(chaiscript::fun(&Class::member_name), "member_name"); ### Bound Member Functions -``` +```cpp chai.add(chaiscript::fun(&Class::method_name, Class_instance_ptr), "method_name"); chai.add(chaiscript::fun(&Class::member_name, Class_instance_ptr), "member_name"); ``` @@ -41,18 +39,18 @@ chai.add(chaiscript::fun(&Class::member_name, Class_instance_ptr), "member_name" #### Preferred -``` +```cpp chai.add(chaiscript::fun(&function_with_overloads), "function_name"); ``` #### Alternative -``` +```cpp chai.add(chaiscript::fun(std::static_cast(&function_with_overloads)), "function_name"); ``` This overload technique is also used when exposing base member using derived type -``` +```cpp struct Base { int data; @@ -66,9 +64,9 @@ chai.add(chaiscript::fun(static_cast(&Derived::data)), "data"); ### Lambda -``` +```cpp chai.add( - chaiscript::fun( + chaiscript::fun>( [](bool type) { if (type) { return "x"; } else { return "y"; } @@ -77,7 +75,7 @@ chai.add( ### Constructors -``` +```cpp chai.add(chaiscript::constructor(), "MyType"); chai.add(chaiscript::constructor(), "MyType"); ``` @@ -86,7 +84,7 @@ chai.add(chaiscript::constructor(), "MyType"); It's not strictly necessary to add types, but it helps with many things. Cloning, better errors, etc. -``` +```cpp chai.add(chaiscript::user_type(), "MyClass"); ``` @@ -109,27 +107,27 @@ add_type_conversion(type("string"), type("Type_Info"), fun(s) { return type(s); Invoking a C++ type conversion possible with `static_cast` -``` +```cpp chai.add(chaiscript::type_conversion()); ``` Calling a user defined type conversion that takes a lambda -``` +```cpp chai.add(chaiscript::type_conversion([](const TestBaseType &t_bt) { /* return converted thing */ })); ``` ### Class Hierarchies -If you want objects to be convertable between base and derived classes, you must tell ChaiScritp about the relationship. +If you want objects to be convertable between base and derived classes, you must tell ChaiScript about the relationship. -``` +```cpp chai.add(chaiscript::base_class()); ``` If you have multiple classes in your inheritance graph, you will probably want to tell ChaiScript about all relationships. -``` +```cpp chai.add(chaiscript::base_class()); chai.add(chaiscript::base_class()); chai.add(chaiscript::base_class()); @@ -170,7 +168,8 @@ chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overw Namespaces will not be populated until `import` is called. This saves memory and computing costs if a namespace is not imported into every ChaiScript instance. -``` + +```cpp chai.register_namespace([](chaiscript::Namespace& math) { math["pi"] = chaiscript::const_var(3.14159); math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); }, @@ -186,7 +185,7 @@ print(math.pi) // prints 3.14159 # Using STL ChaiScript recognize many types from STL, but you have to add specific instantiation yourself. -``` +```cpp typedef std::vector> data_list; data_list my_list{ make_pair(0, "Hello"), make_pair(1, "World") }; chai.add(chaiscript::bootstrap::standard_library::vector_type("DataList")); @@ -204,7 +203,7 @@ chai.eval(R"_( ## General -``` +```cpp chai.eval("print(\"Hello World\")"); chai.eval(R"(print("Hello World"))"); ``` @@ -215,13 +214,13 @@ Returns values are of the type `Boxed_Value` which is meant to be opaque to the ### Prefered -``` +```cpp chai.eval("5.3 + 2.1"); // returns 7.4 as a C++ double ``` ### Alternative -``` +```cpp auto v = chai.eval("5.3 + 2.1"); chai.boxed_cast(v); // extracts double value from boxed_value and applies known conversions chaiscript::boxed_cast(v); // free function version, does not know about conversions @@ -229,7 +228,7 @@ chaiscript::boxed_cast(v); // free function version, does not know about ### Converting Between Algebraic Types -``` +```cpp chaiscript::Boxed_Number(chai.eval("5.3 + 2.1")).get_as(); // works with any number type // which is equivalent to, but much more automatic than: static_cast(chai.eval("5.3+2.1")); // this version only works if we know that it's a double @@ -259,7 +258,7 @@ int main() ## Sharing Values -``` +```cpp double &d = chai.eval("var i = 5.2; i"); // d is now a reference to i in the script std::shared_ptr d = chai.eval("var i = 5.2; i"); // same result but reference counted @@ -269,7 +268,7 @@ chai.eval("print(i)"); // prints 3 ## Catching Eval Errors -``` +```cpp try { chai.eval("2.3 + \"String\""); } catch (const chaiscript::exception::eval_error &e) { @@ -279,7 +278,7 @@ try { ## Catching Errors Thrown From Script -``` +```cpp try { chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); } catch (const double e) { @@ -294,19 +293,19 @@ try { ## Sharing Functions -``` +```cpp auto p = chai.eval>("to_string"); p(5); // calls chaiscript's 'to_string' function, returning std::string("5") ``` Note: backtick treats operators as normal functions -``` +```cpp auto p = chai.eval>(`+`); p(5, 6); // calls chaiscript's '+' function, returning 11 ``` -``` +```cpp auto p = chai.eval>("fun(x,y) { to_string(x) + to_string(y); }"); p(3,4.2); // evaluates the lambda function, returning the string "34.2" to C++ ``` @@ -346,7 +345,7 @@ while (some_condition()) { /* do something */ } ``` // ranged for -for (x : [1,2,3]) { print(i); } +for (i : [1, 2, 3]) { print(i); } ``` Each of the loop styles can be broken using the `break` statement. For example: @@ -391,11 +390,23 @@ switch (myvalue) { ## Built in Types +There are a number of build-in types that are part of ChaiScript. + +### Vectors and Maps + ``` var v = [1,2,3u,4ll,"16", `+`]; // creates vector of heterogenous values var m = ["a":1, "b":2]; // map of string:value pairs + +// Add a value to the vector by value. +v.push_back(123); + +// Add an object to the vector by reference. +v.push_back_ref(m); ``` +### Numbers + Floating point values default to `double` type and integers default to `int` type. All C++ suffixes such as `f`, `ll`, `u` as well as scientific notation are supported @@ -566,28 +577,11 @@ If both a 2 parameter and a 3 parameter signature match, the 3 parameter functio * `__LINE__` Current file line number * `__FILE__` Full path of current file * `__CLASS__` Name of current class - * `__FUNC__` Mame of current function + * `__FUNC__` Name of current function # Built In Functions -## Disabling Built-Ins - -When constructing a ChaiScript object, a vector of parameters can be passed in to disable or enable various built-in methods. - -Current options: - -``` -enum class Options -{ - Load_Modules, - No_Load_Modules, - External_Scripts, - No_External_Scripts -}; -``` - - ## Evaluation ``` @@ -603,3 +597,6 @@ Both `use` and `eval_file` search the 'usepaths' passed to the ChaiScript constr * `from_json` converts a JSON string into its strongly typed (map, vector, int, double, string) representations * `to_json` converts a ChaiScript object (either a `Object` or one of map, vector, int, double, string) tree into its JSON string representation + +## Extras +ChaiScript itself does not provide a link to the math functions defined in ``. You can either add them yourself, or use the [ChaiScript_Extras](https://github.com/ChaiScript/ChaiScript_Extras) helper library. (Which also provides some additional string functions.) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index dbf6d73b..871b353b 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -1,31 +1,29 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_HPP_ #define CHAISCRIPT_HPP_ - - /// @mainpage /// [ChaiScript](http://www.chaiscript.com") is a scripting language designed specifically for integration with C++. It provides /// seamless integration with C++ on all levels, including shared_ptr objects, functors and exceptions. -/// -/// The parts of the ChaiScript API that the average user will be concerned with are contained in the +/// +/// The parts of the ChaiScript API that the average user will be concerned with are contained in the /// chaiscript namespace and the chaiscript::ChaiScript class. /// /// The end user parts of the API are extremely simple both in size and ease of use. /// -/// Currently, all source control and project management aspects of ChaiScript occur on [github](http://www.github.com/ChaiScript/ChaiScript"). +/// Currently, all source control and project management aspects of ChaiScript occur on +/// [github](http://www.github.com/ChaiScript/ChaiScript"). /// /// ------------------------------------------------------------ -/// +/// /// @sa chaiscript /// @sa chaiscript::ChaiScript /// @sa ChaiScript_Language for Built in Functions @@ -51,16 +49,16 @@ /// - @ref functionobjects /// - @ref threading /// - @ref exceptions -/// -/// +/// +/// /// @subsection basics Basics -/// +/// /// Basic simple example: /// /// ~~~~~~~{.cpp} /// //main.cpp /// #include -/// +/// /// double function(int i, double j) /// { /// return i * j; @@ -72,7 +70,7 @@ /// chai.add(chaiscript::fun(&function), "function"); /// /// double d = chai.eval("function(3, 4.75);"); -/// } +/// } /// ~~~~~~~ /// /// ------------------------------------------------------ @@ -81,14 +79,14 @@ /// /// ChaiScript is a header only library with only one dependency: The /// operating system provided dynamic library loader, which has to be specified on some platforms. -/// +/// /// @subsubsection compilinggcc Compiling with GCC -/// +/// /// To compile the above application on a Unix like operating system (MacOS, Linux) with GCC you need to link /// the dynamic loader. For example: /// /// ~~~~~~~~ -/// gcc main.cpp -I/path/to/chaiscript/headers -ldl +/// gcc main.cpp -I/path/to/chaiscript/headers -ldl /// ~~~~~~~~ /// /// Alternatively, you may compile without threading support. @@ -111,11 +109,11 @@ /// chaiscript::ChaiScript chai; /// chai("print(@"hello world@")"); /// ~~~~~~~~ -/// +/// /// @sa chaiscript::ChaiScript::operator()(const std::string &) /// /// @subsubsection evalmethod Method 'eval' -/// +/// /// The eval method is somewhat more verbose and can be used to get type safely return values /// from the script. /// @@ -129,15 +127,15 @@ /// @sa chaiscript::ChaiScript::eval /// /// @subsubsection evalfilemethod Method 'eval_file' -/// +/// /// The 'eval_file' method loads a file from disk and executes the script in it -/// +/// /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// chai.eval_file("myfile.chai"); /// std::string result = chai.eval_file("myfile.chai") // extract the last value returned from the file /// ~~~~~~~~ -/// +/// /// @sa chaiscript::ChaiScript::eval_file /// /// -------------------------------------------------- @@ -149,10 +147,10 @@ /// @subsubsection adding_objects Adding Objects /// /// Named objects can be created with the chaiscript::var function. Note: adding a object -/// adds it to the current thread scope, not to a global scope. If you have multiple +/// adds it to the current thread scope, not to a global scope. If you have multiple /// threads that need to access the same variables you will need to add them /// separately for each thread, from the thread itself. -/// +/// /// ~~~~~~~~~{.cpp} /// using namespace chaiscript; /// ChaiScript chai; @@ -167,23 +165,23 @@ /// chai.add(const_var(i), "i"); /// chai("i = 5"); // exception throw, cannot assign const var /// ~~~~~~~~~ -/// +/// /// Named variables can only be accessed from the context they are created in. -/// If you want a global variable, it must be const, and created with the +/// If you want a global variable, it must be const, and created with the /// chaiscript::ChaiScript::add_global_const function. /// /// ~~~~~~~~~{.cpp} /// chai.add_global_const(const_var(i), "i"); /// chai("def somefun() { print(i); }; somefun();"); /// ~~~~~~~~~ -/// +/// /// @subsubsection adding_functions Adding Functions -/// +/// /// Functions, methods and members are all added using the same function: chaiscript::fun. /// /// ~~~~~~~~~{.cpp} /// using namespace chaiscript; -/// +/// /// class MyClass { /// public: /// int memberdata; @@ -193,7 +191,7 @@ /// void overloadedmethod(); /// void overloadedmethod(const std::string &); /// }; -/// +/// /// ChaiScript chai; /// chai.add(fun(&MyClass::memberdata), "memberdata"); /// chai.add(fun(&MyClass::method), "method"); @@ -208,7 +206,7 @@ /// ~~~~~~~~~ /// /// There are also shortcuts built into chaiscript::fun for binding up to the first two parameters of the function. -/// +/// /// ~~~~~~~~~{.cpp} /// MyClass obj; /// chai.add(fun(&MyClass::method, &obj), "method"); @@ -220,7 +218,7 @@ /// @subsubsection addingtypeinfo Adding Type Info /// /// ChaiScript will automatically support any type implicitly provided to it in the form -/// of objects and function parameters / return types. However, it can be nice to let ChaiScript +/// of objects and function parameters / return types. However, it can be nice to let ChaiScript /// know more details about the types you are giving it. For instance, the "clone" functionality /// cannot work unless there is a copy constructor registered and the name of the type is known /// (so that ChaiScript can look up the copy constructor). @@ -232,23 +230,23 @@ /// ~~~~~~~~ /// /// @subsubsection adding_modules Adding Modules -/// +/// /// Modules are holders for collections of ChaiScript registrations. /// /// ~~~~~~~~{.cpp} /// ModulePtr module = get_sum_module(); /// chai.add(module); /// ~~~~~~~~ -/// +/// /// @sa chaiscript::Module /// /// ----------------------------------------------------------------------- /// /// @subsection operatoroverloading Operator Overloading -/// +/// /// Operators are just like any other function in ChaiScript, to overload an operator, simply register it. -/// -/// ~~~~~~~~{.cpp} +/// +/// ~~~~~~~~{.cpp} /// class MyClass { /// MyClass operator+(const MyClass &) const; /// }; @@ -270,7 +268,7 @@ /// ----------------------------------------------------------------------- /// /// @subsection add_class Class Helper Utility -/// +/// /// Much of the work of adding new classes to ChaiScript can be reduced with the help /// of the add_class helper utility. /// @@ -295,8 +293,8 @@ /// constructor() }, /// { {fun(&Test::function), "function"}, /// {fun(&Test::function2), "function2"}, -/// {fun(&Test::function2), "function3"} -/// {fun(static_cast(&Test::functionOverload)), "functionOverload"} +/// {fun(&Test::function2), "function3"} +/// {fun(static_cast(&Test::functionOverload)), "functionOverload"} /// {fun(static_cast(&Test::functionOverload)), "functionOverload"} } /// ); /// @@ -305,7 +303,7 @@ /// chai.add(m); /// } /// ~~~~~~~~ -/// +/// /// @sa @ref adding_modules /// /// ----------------------------------------------------------------------- @@ -315,8 +313,8 @@ /// As much as possible, ChaiScript attempts to convert between &, *, const &, const *, std::shared_ptr, /// std::shared_ptr, std::reference_wrapper, std::reference_wrapper and value types automatically. /// -/// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference counting). -/// Const may be added, but never removed. +/// If a chaiscript::var object was created in C++ from a pointer, it cannot be converted to a shared_ptr (this would add invalid reference +/// counting). Const may be added, but never removed. /// /// The take away is that you can pretty much expect function calls to Just Work when you need them to. /// @@ -359,16 +357,16 @@ /// chai("fun8(i)"); /// chai("fun9(i)"); /// chai("fun10(i)"); -/// } +/// } /// ~~~~~~~~ /// -/// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of the automatic casts that +/// See the unit test unittests/boxed_cast_test.cpp for a complete breakdown of the automatic casts that /// available and tested. -/// +/// /// ----------------------------------------------------------------------- /// /// @subsection baseclasses Base Classes -/// +/// /// ChaiScript supports handling of passing a derived class object to a function expecting a base class object. /// For the process to work, the base/derived relationship must be registered with the engine. /// @@ -387,7 +385,7 @@ /// chai("myfunction(d)"); /// } /// ~~~~~~~~ -/// +/// /// ----------------------------------------------------------------------- /// /// @@ -401,7 +399,7 @@ /// { /// t_func("bob"); /// } -/// +/// /// int main() /// { /// chaiscript::ChaiScript chai; @@ -413,16 +411,16 @@ /// f(); // call the ChaiScript function dump_system, from C++ /// } /// ~~~~~~~~ -/// +/// /// ----------------------------------------------------------------------- /// /// /// @subsection threading Threading -/// +/// /// Thread safety is automatically handled within the ChaiScript system. Objects can be added /// and scripts executed from multiple threads. For each thread that executes scripts, a new /// context is created and managed by the engine. -/// +/// /// Thread safety can be disabled by defining CHAISCRIPT_NO_THREADS when using the library. /// /// Disabling thread safety increases performance in many cases. @@ -431,10 +429,10 @@ /// /// /// @subsection exceptions Exception Handling -/// +/// /// @subsubsection exceptionsbasics Exception Handling Basics /// -/// Exceptions can be thrown in ChaiScript and caught in C++ or thrown in C++ and caught in +/// Exceptions can be thrown in ChaiScript and caught in C++ or thrown in C++ and caught in /// ChaiScript. /// /// ~~~~~~~~{.cpp} @@ -442,14 +440,14 @@ /// { /// throw std::runtime_error("err"); /// } -/// +/// /// int main() /// { /// // Throw in C++, catch in ChaiScript /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::fun(&throwexception), "throwexception"); /// chai("try { throwexception(); } catch (e) { print(e.what()); }"); // prints "err" -/// +/// /// // Throw in ChaiScript, catch in C++ /// try { /// chai("throw(1)"); @@ -459,18 +457,19 @@ /// } /// } /// ~~~~~~~~ -/// +/// /// @subsubsection exceptionsautomatic Exception Handling Automatic Unboxing /// -/// As an alternative to the manual unboxing of exceptions shown above, exception specifications allow the user to tell -/// ChaiScript what possible exceptions are expected from the script being executed. +/// As an alternative to the manual unboxing of exceptions shown above, exception specifications allow the user to tell +/// ChaiScript what possible exceptions are expected from the script being executed. /// /// Example: /// ~~~~~~~~{.cpp} /// chaiscript::ChaiScript chai; /// /// try { -/// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification()); +/// chai.eval("throw(runtime_error(@"error@"))", chaiscript::exception_specification()); /// } catch (const double e) { /// } catch (int) { /// } catch (float) { @@ -480,11 +479,9 @@ /// } /// ~~~~~~~~ /// -/// @sa chaiscript::Exception_Handler for details on automatic exception unboxing +/// @sa chaiscript::Exception_Handler for details on automatic exception unboxing /// @sa chaiscript::exception_specification - - /// @page LangObjectSystemRef ChaiScript Language Object Model Reference /// /// @@ -495,7 +492,7 @@ /// attr Rectangle::width /// def Rectangle::Rectangle() { this.height = 10; this.width = 20 } /// def Rectangle::area() { this.height * this.width } -/// +/// /// var rect = Rectangle() /// rect.height = 30 /// print(rect.area()) @@ -521,7 +518,7 @@ /// @page LangInPlaceRef ChaiScript Language In-Place Creation Reference /// @section inplacevector Vector -/// +/// /// ~~~~~~~~~ /// In-place Vector ::= "[" [expression ("," expression)*] "]" /// ~~~~~~~~~ @@ -541,9 +538,9 @@ /// ~~~~~~~~ /// @page LangGettingStarted ChaiScript Language Getting Started -/// +/// /// ChaiScript is a simple language that should feel familiar to anyone who knows -/// C++ or ECMAScript (JavaScript). +/// C++ or ECMAScript (JavaScript). /// /// ----------------------------------------------------------------------- /// @@ -575,10 +572,10 @@ /// @section chaiscriptifs Conditionals /// /// If statements work as expected -/// +/// /// ~~~~~~~~ /// var b = true; -/// +/// /// if (b) { /// // do something /// } else if (c < 10) { @@ -593,9 +590,9 @@ /// ----------------------------------------------------------------------- /// /// @section chaiscriptfunctions Functions -/// +/// /// Functions are defined with the def keyword -/// +/// /// ~~~~~~~~ /// def myfun(x) { print(x); } /// @@ -612,7 +609,7 @@ /// eval> myfun2(12) /// 10 or greater /// ~~~~~~~~ -/// +/// /// @sa @ref keyworddef /// @sa @ref keywordattr /// @sa @ref LangObjectSystemRef @@ -620,7 +617,7 @@ /// ----------------------------------------------------------------------- /// /// @section chaiscriptfunctionobjects Function Objects -/// +/// /// Functions are first class types in ChaiScript and can be used as variables. /// /// ~~~~~~~~ @@ -628,8 +625,8 @@ /// eval> p(1); /// 1 /// ~~~~~~~~ -/// -/// They can also be passed to functions. +/// +/// They can also be passed to functions. /// /// ~~~~~~~~ /// eval> def callfunc(f, lhs, rhs) { return f(lhs, rhs); } @@ -639,7 +636,7 @@ /// ~~~~~~~~ /// /// Operators can also be treated as functions by using the back tick operator. Building on the above example: -/// +/// /// ~~~~~~~~ /// eval> callfunc(`+`, 1, 4); /// 5 @@ -652,7 +649,6 @@ /// @sa @ref LangKeywordRef /// @sa ChaiScript_Language for Built in Functions - /// @page LangKeywordRef ChaiScript Language Keyword Reference /// /// @@ -660,8 +656,8 @@ /// /// @section keywordattr attr /// Defines a ChaiScript object attribute -/// -/// ~~~~~~~~ +/// +/// ~~~~~~~~ /// Attribute Definition ::= "attr" class_name "::" attribute_name /// ~~~~~~~~ /// @@ -690,7 +686,7 @@ /// ~~~~~~~~ /// /// @sa @ref keywordfor -/// @sa @ref keywordwhile +/// @sa @ref keywordwhile /// /// /// ----------------------------------------------------------------------- @@ -702,7 +698,7 @@ /// Function Definition ::= "def" identifier "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// Method Definition ::= "def" class_name "::" method_name "(" [[type] arg ("," [type] arg)*] ")" [":" guard] block /// ~~~~~~~~ -/// +/// /// identifier: name of function. Required. /// args: comma-delimited list of parameter names with optional type specifiers. Optional. /// guards: guarding statement that act as a prerequisite for the function. Optional. @@ -722,7 +718,7 @@ /// ----------------------------------------------------------------------- /// /// @section keywordelse else -/// @sa @ref keywordif +/// @sa @ref keywordif /// /// /// ----------------------------------------------------------------------- @@ -762,7 +758,7 @@ /// Else If Block ::= "else if" "(" condition ")" block /// Else Block ::= "else" block /// ~~~~~~~~ -/// +/// /// _Example_ /// /// ~~~~~~~~ @@ -780,8 +776,8 @@ /// /// @section keywordtry try /// ~~~~~~~~ -/// Try Block ::= "try" block -/// ("catch" ["(" [type] variable ")"] [":" guards] block)+ +/// Try Block ::= "try" block +/// ("catch" ["(" [type] variable ")"] [":" guards] block)+ /// ["finally" block] /// ~~~~~~~~ /// @@ -791,7 +787,7 @@ /// ----------------------------------------------------------------------- /// /// @section keywordwhile while -/// +/// /// Begins a conditional block of code that loops 0 or more times, as long as the condition is true /// /// ~~~~~~~~ @@ -804,7 +800,7 @@ /// ----------------------------------------------------------------------- /// /// @section keywordvar var -/// +/// /// Defines a variable /// /// ~~~~~~~~ @@ -813,7 +809,6 @@ /// /// Synonym for @ref keywordauto - /// @namespace chaiscript /// @brief Namespace chaiscript contains every API call that the average user will be concerned with. @@ -821,25 +816,22 @@ /// @brief Classes and functions reserved for internal use. Items in this namespace are not supported. #include "chaiscript_basic.hpp" -#include "language/chaiscript_parser.hpp" #include "chaiscript_stdlib.hpp" +#include "language/chaiscript_parser.hpp" - -namespace chaiscript -{ - class ChaiScript : public ChaiScript_Basic - { - public: - ChaiScript(std::vector t_modulepaths = {}, - std::vector t_usepaths = {}, - const std::vector &t_opts = chaiscript::default_options()) - : ChaiScript_Basic( - chaiscript::Std_Lib::library(), - std::make_unique>(), - t_modulepaths, t_usepaths, t_opts) - { - } +namespace chaiscript { + class ChaiScript : public ChaiScript_Basic { + public: + ChaiScript(std::vector t_modulepaths = {}, + std::vector t_usepaths = {}, + std::vector t_opts = chaiscript::default_options()) + : ChaiScript_Basic(chaiscript::Std_Lib::library(), + std::make_unique>(), + std::move(t_modulepaths), + std::move(t_usepaths), + std::move(t_opts)) { + } }; -} +} // namespace chaiscript #endif /* CHAISCRIPT_HPP_ */ diff --git a/include/chaiscript/chaiscript_basic.hpp b/include/chaiscript/chaiscript_basic.hpp index b770b1b7..a75c731b 100644 --- a/include/chaiscript/chaiscript_basic.hpp +++ b/include/chaiscript/chaiscript_basic.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_BASIC_HPP_ @@ -9,13 +9,13 @@ #include "chaiscript_defines.hpp" -#include "dispatchkit/dispatchkit.hpp" -#include "dispatchkit/function_call.hpp" -#include "dispatchkit/dynamic_object.hpp" #include "dispatchkit/boxed_number.hpp" +#include "dispatchkit/dispatchkit.hpp" +#include "dispatchkit/dynamic_object.hpp" +#include "dispatchkit/function_call.hpp" -#include "language/chaiscript_eval.hpp" #include "language/chaiscript_engine.hpp" +#include "language/chaiscript_eval.hpp" // This file includes all of the basic requirements for ChaiScript, // to use, you might do something like: @@ -34,6 +34,4 @@ ChaiScript_Basic chai( // If you want a fully packaged ready to go ChaiScript, use chaiscript.hpp - - #endif /* CHAISCRIPT_BASIC_HPP_ */ diff --git a/include/chaiscript/chaiscript_defines.hpp b/include/chaiscript/chaiscript_defines.hpp index 4508d603..e01aadd3 100644 --- a/include/chaiscript/chaiscript_defines.hpp +++ b/include/chaiscript/chaiscript_defines.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DEFINES_HPP_ @@ -20,9 +20,10 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req #define CHAISCRIPT_COMPILER_VERSION __VERSION__ #endif +#include #include -#if defined( _LIBCPP_VERSION ) +#if defined(_LIBCPP_VERSION) #define CHAISCRIPT_LIBCPP #endif @@ -48,16 +49,14 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req #endif #endif - #if defined(__llvm__) #define CHAISCRIPT_CLANG #endif - -#ifdef CHAISCRIPT_HAS_DECLSPEC +#ifdef CHAISCRIPT_HAS_DECLSPEC #define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport) #else -#define CHAISCRIPT_MODULE_EXPORT extern "C" +#define CHAISCRIPT_MODULE_EXPORT extern "C" #endif #if defined(CHAISCRIPT_MSVC) || (defined(__GNUC__) && __GNUC__ >= 5) || defined(CHAISCRIPT_CLANG) @@ -70,176 +69,176 @@ static_assert(_MSC_FULL_VER >= 190024210, "Visual C++ 2015 Update 3 or later req #define CHAISCRIPT_DEBUG false #endif +#include #include #include -#include namespace chaiscript { - static const int version_major = 6; - static const int version_minor = 1; - static const int version_patch = 0; + constexpr static const int version_major = 7; + constexpr static const int version_minor = 0; + constexpr static const int version_patch = 0; - static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION; - static const char *compiler_name = CHAISCRIPT_COMPILER_NAME; - static const bool debug_build = CHAISCRIPT_DEBUG; + constexpr static const char *compiler_version = CHAISCRIPT_COMPILER_VERSION; + constexpr static const char *compiler_name = CHAISCRIPT_COMPILER_NAME; + constexpr static const bool debug_build = CHAISCRIPT_DEBUG; - template - inline std::shared_ptr make_shared(Arg && ... arg) - { + template + inline std::shared_ptr make_shared(Arg &&...arg) { #ifdef CHAISCRIPT_USE_STD_MAKE_SHARED return std::make_shared(std::forward(arg)...); #else - return std::shared_ptr(static_cast(new D(std::forward(arg)...))); + return std::shared_ptr(static_cast(new D(std::forward(arg)...))); #endif } - template - inline std::unique_ptr make_unique(Arg && ... arg) - { + template + inline std::unique_ptr make_unique(Arg &&...arg) { #ifdef CHAISCRIPT_USE_STD_MAKE_SHARED return std::make_unique(std::forward(arg)...); #else - return std::unique_ptr(static_cast(new D(std::forward(arg)...))); + return std::unique_ptr(static_cast(new D(std::forward(arg)...))); #endif } struct Build_Info { - static int version_major() - { - return chaiscript::version_major; - } + [[nodiscard]] constexpr static int version_major() noexcept { return chaiscript::version_major; } - static int version_minor() - { - return chaiscript::version_minor; - } + [[nodiscard]] constexpr static int version_minor() noexcept { return chaiscript::version_minor; } - static int version_patch() - { - return chaiscript::version_patch; - } + [[nodiscard]] constexpr static int version_patch() noexcept { return chaiscript::version_patch; } - static std::string version() - { + [[nodiscard]] static std::string version() { return std::to_string(version_major()) + '.' + std::to_string(version_minor()) + '.' + std::to_string(version_patch()); } - static std::string compiler_id() - { - return compiler_name() + '-' + compiler_version(); - } + [[nodiscard]] static std::string compiler_id() { return compiler_name() + '-' + compiler_version(); } - static std::string build_id() - { - return compiler_id() + (debug_build()?"-Debug":"-Release"); - } + [[nodiscard]] static std::string build_id() { return compiler_id() + (debug_build() ? "-Debug" : "-Release"); } - static std::string compiler_version() - { - return chaiscript::compiler_version; - } + [[nodiscard]] static std::string compiler_version() { return chaiscript::compiler_version; } - static std::string compiler_name() - { - return chaiscript::compiler_name; - } + [[nodiscard]] static std::string compiler_name() { return chaiscript::compiler_name; } - static bool debug_build() - { - return chaiscript::debug_build; - } + [[nodiscard]] constexpr static bool debug_build() noexcept { return chaiscript::debug_build; } }; - template - auto parse_num(const char *t_str) -> typename std::enable_if::value, T>::type - { - T t = 0; - for (char c = *t_str; (c = *t_str) != 0; ++t_str) { - if (c < '0' || c > '9') { - return t; - } - t *= 10; - t += c - '0'; + [[nodiscard]] constexpr auto parse_num(const std::string_view t_str) noexcept -> typename std::enable_if::value, T>::type { + T t = 0; + for (const auto c : t_str) { + if (c < '0' || c > '9') { + return t; } - return t; - } - - - template - auto parse_num(const char *t_str) -> typename std::enable_if::value, T>::type - { - T t = 0; - T base{}; - T decimal_place = 0; - int exponent = 0; - - for (char c;; ++t_str) { - c = *t_str; - switch (c) - { - case '.': - decimal_place = 10; - break; - case 'e': - case 'E': - exponent = 1; - decimal_place = 0; - base = t; - t = 0; - break; - case '-': - exponent = -1; - break; - case '+': - break; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - if (decimal_place < 10) { - t *= 10; - t += static_cast(c - '0'); - } - else { - t += static_cast(c - '0') / decimal_place; - decimal_place *= 10; - } - break; - default: - return exponent ? base * std::pow(T(10), t * static_cast(exponent)) : t; - } - } + t *= 10; + t += c - '0'; } + return t; + } template - T parse_num(const std::string &t_str) - { - return parse_num(t_str.c_str()); - } + [[nodiscard]] auto parse_num(const std::string_view t_str) -> typename std::enable_if::value, T>::type { + T t = 0; + T base{}; + T decimal_place = 0; + int exponent = 0; - enum class Options - { + for (const auto c : t_str) { + switch (c) { + case '.': + decimal_place = 10; + break; + case 'e': + case 'E': + exponent = 1; + decimal_place = 0; + base = t; + t = 0; + break; + case '-': + exponent = -1; + break; + case '+': + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if (decimal_place < 10) { + t *= 10; + t += static_cast(c - '0'); + } else { + t += static_cast(c - '0') / decimal_place; + decimal_place *= 10; + } + break; + default: + break; + } + } + return exponent ? base * std::pow(T(10), t * static_cast(exponent)) : t; + } + + struct str_equal { + [[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { return t_lhs == t_rhs; } + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept { + return std::equal(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end()); + } + struct is_transparent { + }; + }; + + struct str_less { + [[nodiscard]] bool operator()(const std::string &t_lhs, const std::string &t_rhs) const noexcept { return t_lhs < t_rhs; } + template + [[nodiscard]] constexpr bool operator()(const LHS &t_lhs, const RHS &t_rhs) const noexcept { + return std::lexicographical_compare(t_lhs.begin(), t_lhs.end(), t_rhs.begin(), t_rhs.end()); + } + struct is_transparent { + }; + }; + + enum class Options { No_Load_Modules, Load_Modules, No_External_Scripts, External_Scripts }; - static inline std::vector default_options() - { + template + struct is_nothrow_forward_constructible : std::bool_constant()})> { + }; + + template + static inline constexpr bool is_nothrow_forward_constructible_v = is_nothrow_forward_constructible::value; + + template + [[nodiscard]] constexpr auto make_container(T &&...t) { + Container c; + c.reserve(sizeof...(t)); + (c.push_back(std::forward(t)), ...); + return c; + } + + template + [[nodiscard]] auto make_vector(T &&...t) -> std::vector...>> { + using container_type = std::vector...>>; + + return make_container(std::forward(t)...); + } + + [[nodiscard]] inline std::vector default_options() { #ifdef CHAISCRIPT_NO_DYNLOAD return {Options::No_Load_Modules, Options::External_Scripts}; #else return {Options::Load_Modules, Options::External_Scripts}; #endif } -} +} // namespace chaiscript #endif - diff --git a/include/chaiscript/chaiscript_stdlib.hpp b/include/chaiscript/chaiscript_stdlib.hpp index ed56b234..5cb4fa68 100644 --- a/include/chaiscript/chaiscript_stdlib.hpp +++ b/include/chaiscript/chaiscript_stdlib.hpp @@ -19,53 +19,48 @@ #include "dispatchkit/function_call.hpp" //#include "dispatchkit/dispatchkit.hpp" -#include "dispatchkit/operators.hpp" #include "dispatchkit/bootstrap.hpp" #include "dispatchkit/bootstrap_stl.hpp" +#include "dispatchkit/operators.hpp" //#include "dispatchkit/boxed_value.hpp" -#include "language/chaiscript_prelude.hpp" #include "dispatchkit/register_function.hpp" +#include "language/chaiscript_prelude.hpp" #include "utility/json_wrap.hpp" #ifndef CHAISCRIPT_NO_THREADS #include #endif - /// @file /// /// This file generates the standard library that normal ChaiScript usage requires. -namespace chaiscript -{ - class Std_Lib - { - public: +namespace chaiscript { + class Std_Lib { + public: + [[nodiscard]] static ModulePtr library() { + auto lib = std::make_shared(); + bootstrap::Bootstrap::bootstrap(*lib); - static ModulePtr library() - { - auto lib = std::make_shared(); - bootstrap::Bootstrap::bootstrap(*lib); - - bootstrap::standard_library::vector_type >("Vector", *lib); - bootstrap::standard_library::string_type("string", *lib); - bootstrap::standard_library::map_type >("Map", *lib); - bootstrap::standard_library::pair_type >("Pair", *lib); + bootstrap::standard_library::vector_type>("Vector", *lib); + bootstrap::standard_library::string_type("string", *lib); + bootstrap::standard_library::map_type>("Map", *lib); + bootstrap::standard_library::pair_type>("Pair", *lib); #ifndef CHAISCRIPT_NO_THREADS - bootstrap::standard_library::future_type>("future", *lib); - lib->add(chaiscript::fun([](const std::function &t_func){ return std::async(std::launch::async, t_func);}), "async"); + bootstrap::standard_library::future_type>("future", *lib); + lib->add(chaiscript::fun( + [](const std::function &t_func) { return std::async(std::launch::async, t_func); }), + "async"); #endif - json_wrap::library(*lib); + json_wrap::library(*lib); - lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ ); - - return lib; - } + lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/); + return lib; + } }; -} +} // namespace chaiscript #endif - diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index 79feaa1e..d0145c87 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -1,25 +1,24 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_THREADING_HPP_ #define CHAISCRIPT_THREADING_HPP_ - #include #ifndef CHAISCRIPT_NO_THREADS -#include #include +#include +#include #else #ifndef CHAISCRIPT_NO_THREADS_WARNING -#pragma message ("ChaiScript is compiling without thread safety.") +#pragma message("ChaiScript is compiling without thread safety.") #endif #endif @@ -33,141 +32,102 @@ /// It also has the side effect that the chaiscript::ChaiScript object may not be accessed from more than /// one thread simultaneously. -namespace chaiscript -{ - namespace detail - { - /// If threading is enabled, then this namespace contains std thread classes. - /// If threading is not enabled, then stubbed in wrappers that do nothing are provided. - /// This allows us to avoid \#ifdef code in the sections that need thread safety. - namespace threading - { - +/// If threading is enabled, then this namespace contains std thread classes. +/// If threading is not enabled, then stubbed in wrappers that do nothing are provided. +/// This allows us to avoid \#ifdef code in the sections that need thread safety. +namespace chaiscript::detail::threading { #ifndef CHAISCRIPT_NO_THREADS - template - using unique_lock = std::unique_lock; + template + using unique_lock = std::unique_lock; - template - using shared_lock = std::unique_lock; + template + using shared_lock = std::shared_lock; - template - using lock_guard = std::lock_guard; + template + using lock_guard = std::lock_guard; + using std::shared_mutex; - using shared_mutex = std::mutex; + using std::mutex; - using std::mutex; + using std::recursive_mutex; - using std::recursive_mutex; + /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If + /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. + template + class Thread_Storage { + public: + Thread_Storage() = default; + Thread_Storage(const Thread_Storage &) = delete; + Thread_Storage(Thread_Storage &&) = delete; + Thread_Storage &operator=(const Thread_Storage &) = delete; + Thread_Storage &operator=(Thread_Storage &&) = delete; - /// Typesafe thread specific storage. If threading is enabled, this class uses a mutex protected map. If - /// threading is not enabled, the class always returns the same data, regardless of which thread it is called from. - template - class Thread_Storage - { - public: - Thread_Storage() = default; - Thread_Storage(const Thread_Storage &) = delete; - Thread_Storage(Thread_Storage &&) = delete; - Thread_Storage &operator=(const Thread_Storage &) = delete; - Thread_Storage &operator=(Thread_Storage &&) = delete; + ~Thread_Storage() { t().erase(this); } - ~Thread_Storage() - { - t().erase(this); - } + inline const T *operator->() const noexcept { return &(t()[this]); } - inline const T *operator->() const - { - return &(t()[this]); - } + inline const T &operator*() const noexcept { return t()[this]; } - inline const T &operator*() const - { - return t()[this]; - } + inline T *operator->() noexcept { return &(t()[this]); } - inline T *operator->() - { - return &(t()[this]); - } + inline T &operator*() noexcept { return t()[this]; } - inline T &operator*() - { - return t()[this]; - } + void *m_key; - - void *m_key; - - private: - static std::unordered_map &t() - { - thread_local std::unordered_map my_t; - return my_t; - } - }; + private: + /// todo: is it valid to make this noexcept? The allocation could fail, but if it + /// does there is no possible way to recover + static std::unordered_map &t() noexcept { + static thread_local std::unordered_map my_t; + return my_t; + } + }; #else // threading disabled - template - class unique_lock - { - public: - explicit unique_lock(T &) {} - void lock() {} - void unlock() {} - }; + template + class unique_lock { + public: + constexpr explicit unique_lock(T &) noexcept {} + constexpr void lock() noexcept {} + constexpr void unlock() noexcept {} + }; - template - class shared_lock - { - public: - explicit shared_lock(T &) {} - void lock() {} - void unlock() {} - }; + template + class shared_lock { + public: + constexpr explicit shared_lock(T &) noexcept {} + constexpr void lock() noexcept {} + constexpr void unlock() noexcept {} + }; - template - class lock_guard - { - public: - explicit lock_guard(T &) {} - }; + template + class lock_guard { + public: + constexpr explicit lock_guard(T &) noexcept {} + }; - class shared_mutex { }; + class shared_mutex { + }; - class recursive_mutex {}; + class recursive_mutex { + }; + template + class Thread_Storage { + public: + constexpr explicit Thread_Storage() noexcept {} - template - class Thread_Storage - { - public: - explicit Thread_Storage() - { - } + constexpr inline T *operator->() const noexcept { return &obj; } - inline T *operator->() const - { - return &obj; - } + constexpr inline T &operator*() const noexcept { return obj; } - inline T &operator*() const - { - return obj; - } - - private: - mutable T obj; - }; + private: + mutable T obj; + }; #endif - } - } -} - - +} // namespace chaiscript::detail::threading #endif - diff --git a/include/chaiscript/dispatchkit/any.hpp b/include/chaiscript/dispatchkit/any.hpp index ae8035c9..0b1f5b13 100644 --- a/include/chaiscript/dispatchkit/any.hpp +++ b/include/chaiscript/dispatchkit/any.hpp @@ -11,154 +11,105 @@ namespace chaiscript { namespace detail { - namespace exception - { + namespace exception { /// \brief Thrown in the event that an Any cannot be cast to the desired type /// /// It is used internally during function dispatch. /// /// \sa chaiscript::detail::Any - class bad_any_cast : public std::bad_cast - { - public: - bad_any_cast() = default; - - bad_any_cast(const bad_any_cast &) = default; - - ~bad_any_cast() noexcept override = default; - - /// \brief Description of what error occurred - const char * what() const noexcept override - { - return m_what.c_str(); - } - - private: - std::string m_what = "bad any cast"; + class bad_any_cast : public std::bad_cast { + public: + /// \brief Description of what error occurred + const char *what() const noexcept override { return "bad any cast"; } }; - } - + } // namespace exception class Any { - private: - struct Data - { - explicit Data(const std::type_info &t_type) - : m_type(t_type) - { - } - - Data &operator=(const Data &) = delete; - - virtual ~Data() = default; - - virtual void *data() = 0; - - const std::type_info &type() const - { - return m_type; - } - - virtual std::unique_ptr clone() const = 0; - const std::type_info &m_type; - }; - - template - struct Data_Impl : Data - { - explicit Data_Impl(T t_type) - : Data(typeid(T)), - m_data(std::move(t_type)) - { - } - - void *data() override - { - return &m_data; - } - - std::unique_ptr clone() const override - { - return std::unique_ptr(new Data_Impl(m_data)); - } - - Data_Impl &operator=(const Data_Impl&) = delete; - - T m_data; - }; - - std::unique_ptr m_data; - - public: - // construct/copy/destruct - Any() = default; - Any(Any &&) = default; - Any &operator=(Any &&t_any) = default; - - Any(const Any &t_any) - { - if (!t_any.empty()) - { - m_data = t_any.m_data->clone(); - } else { - m_data.reset(); - } + private: + struct Data { + constexpr explicit Data(const std::type_info &t_type) noexcept + : m_type(t_type) { } + Data &operator=(const Data &) = delete; - template::type>::value>::type> - explicit Any(ValueType &&t_value) - : m_data(std::unique_ptr(new Data_Impl::type>(std::forward(t_value)))) - { + virtual ~Data() noexcept = default; + + virtual void *data() noexcept = 0; + + const std::type_info &type() const noexcept { return m_type; } + + virtual std::unique_ptr clone() const = 0; + const std::type_info &m_type; + }; + + template + struct Data_Impl : Data { + explicit Data_Impl(T t_type) + : Data(typeid(T)) + , m_data(std::move(t_type)) { } + void *data() noexcept override { return &m_data; } - Any & operator=(const Any &t_any) - { - Any copy(t_any); - swap(copy); - return *this; + std::unique_ptr clone() const override { return std::make_unique>(m_data); } + + Data_Impl &operator=(const Data_Impl &) = delete; + + T m_data; + }; + + std::unique_ptr m_data; + + public: + // construct/copy/destruct + constexpr Any() noexcept = default; + Any(Any &&) noexcept = default; + Any &operator=(Any &&t_any) = default; + + Any(const Any &t_any) + : m_data(t_any.empty() ? nullptr : t_any.m_data->clone()) { + } + + template>>> + explicit Any(ValueType &&t_value) + : m_data(std::make_unique>>(std::forward(t_value))) { + } + + Any &operator=(const Any &t_any) { + Any copy(t_any); + swap(copy); + return *this; + } + + template + ToType &cast() const { + if (m_data && typeid(ToType) == m_data->type()) { + return *static_cast(m_data->data()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); } + } - template - ToType &cast() const - { - if (m_data && typeid(ToType) == m_data->type()) - { - return *static_cast(m_data->data()); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } + // modifiers + Any &swap(Any &t_other) { + std::swap(t_other.m_data, m_data); + return *this; + } + // queries + bool empty() const noexcept { return !static_cast(m_data); } - // modifiers - Any & swap(Any &t_other) - { - std::swap(t_other.m_data, m_data); - return *this; - } - - // queries - bool empty() const - { - return !bool(m_data); - } - - const std::type_info & type() const - { - if (m_data) { - return m_data->type(); - } else { - return typeid(void); - } + const std::type_info &type() const noexcept { + if (m_data) { + return m_data->type(); + } else { + return typeid(void); } + } }; - } -} + } // namespace detail +} // namespace chaiscript #endif - - diff --git a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp index 1c1d729e..d3b22167 100644 --- a/include/chaiscript/dispatchkit/bad_boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/bad_boxed_cast.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BAD_BOXED_CAST_HPP_ #define CHAISCRIPT_BAD_BOXED_CAST_HPP_ @@ -15,59 +14,51 @@ #include #include "../chaiscript_defines.hpp" +#include "../utility/static_string.hpp" #include "type_info.hpp" namespace chaiscript { -class Type_Info; -} // namespace chaiscript + class Type_Info; +} // namespace chaiscript -namespace chaiscript -{ - namespace exception - { +namespace chaiscript { + namespace exception { /// \brief Thrown in the event that a Boxed_Value cannot be cast to the desired type /// /// It is used internally during function dispatch and may be used by the end user. /// /// \sa chaiscript::boxed_cast - class bad_boxed_cast : public std::bad_cast - { - public: - bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, - std::string t_what) noexcept - : from(t_from), to(&t_to), m_what(std::move(t_what)) - { - } + class bad_boxed_cast : public std::bad_cast { + public: + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to, utility::Static_String t_what) noexcept + : from(t_from) + , to(&t_to) + , m_what(std::move(t_what)) { + } - bad_boxed_cast(Type_Info t_from, const std::type_info &t_to) - : from(t_from), to(&t_to), m_what("Cannot perform boxed_cast: " + t_from.name() + " to: " + t_to.name()) - { - } + bad_boxed_cast(Type_Info t_from, const std::type_info &t_to) noexcept + : from(t_from) + , to(&t_to) + , m_what("Cannot perform boxed_cast") { + } - explicit bad_boxed_cast(std::string t_what) noexcept - : m_what(std::move(t_what)) - { - } + explicit bad_boxed_cast(utility::Static_String t_what) noexcept + : m_what(std::move(t_what)) { + } - bad_boxed_cast(const bad_boxed_cast &) = default; - ~bad_boxed_cast() noexcept override = default; + bad_boxed_cast(const bad_boxed_cast &) noexcept = default; + ~bad_boxed_cast() noexcept override = default; - /// \brief Description of what error occurred - const char * what() const noexcept override - { - return m_what.c_str(); - } + /// \brief Description of what error occurred + const char *what() const noexcept override { return m_what.c_str(); } - Type_Info from; ///< Type_Info contained in the Boxed_Value - const std::type_info *to = nullptr; ///< std::type_info of the desired (but failed) result type + Type_Info from; ///< Type_Info contained in the Boxed_Value + const std::type_info *to = nullptr; ///< std::type_info of the desired (but failed) result type - private: - std::string m_what; + private: + utility::Static_String m_what; }; - } -} - - + } // namespace exception +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/bind_first.hpp b/include/chaiscript/dispatchkit/bind_first.hpp index c65c8385..a9815193 100644 --- a/include/chaiscript/dispatchkit/bind_first.hpp +++ b/include/chaiscript/dispatchkit/bind_first.hpp @@ -1,85 +1,60 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BIND_FIRST_HPP_ #define CHAISCRIPT_BIND_FIRST_HPP_ #include -namespace chaiscript -{ - namespace detail - { +namespace chaiscript { + namespace detail { + template + constexpr T *get_pointer(T *t) noexcept { + return t; + } template - T* get_pointer(T *t) - { - return t; - } + T *get_pointer(const std::reference_wrapper &t) noexcept { + return &t.get(); + } - template - T* get_pointer(const std::reference_wrapper &t) - { - return &t.get(); - } + template + constexpr auto bind_first(Ret (*f)(P1, Param...), O &&o) { + return [f, o = std::forward(o)](Param... param) -> Ret { return f(o, std::forward(param)...); }; + } - template - auto bind_first(Ret (*f)(P1, Param...), O&& o) - { - return [f, o](Param...param) -> Ret { - return f(std::forward(o), std::forward(param)...); - }; - } + template + constexpr auto bind_first(Ret (Class::*f)(Param...), O &&o) { + return [f, o = std::forward(o)](Param... param) -> Ret { return (get_pointer(o)->*f)(std::forward(param)...); }; + } - template - auto bind_first(Ret (Class::*f)(Param...), O&& o) - { - return [f, o](Param...param) -> Ret { - return (get_pointer(o)->*f)(std::forward(param)...); - }; - } + template + constexpr auto bind_first(Ret (Class::*f)(Param...) const, O &&o) { + return [f, o = std::forward(o)](Param... param) -> Ret { return (get_pointer(o)->*f)(std::forward(param)...); }; + } - template - auto bind_first(Ret (Class::*f)(Param...) const, O&& o) - { - return [f, o](Param...param) -> Ret { - return (get_pointer(o)->*f)(std::forward(param)...); - }; + template + auto bind_first(const std::function &f, O &&o) { + return [f, o = std::forward(o)](Param... param) -> Ret { return f(o, std::forward(param)...); }; + } - } - - template - auto bind_first(const std::function &f, O&& o) - { - return [f, o](Param...param) -> Ret { - return f(o, std::forward(param)...); - }; - } - - template - auto bind_first(const F &fo, O&& o, Ret (Class::*f)(P1, Param...) const) - { - return [fo, o, f](Param ...param) -> Ret { - return (fo.*f)(o, std::forward(param)...); - }; - - } + template + constexpr auto bind_first(const F &fo, O &&o, Ret (Class::*f)(P1, Param...) const) { + return [fo, o = std::forward(o), f](Param... param) -> Ret { return (fo.*f)(o, std::forward(param)...); }; + } template - auto bind_first(const F &f, O&& o) - { - return bind_first(f, std::forward(o), &F::operator()); - } - - } -} + constexpr auto bind_first(const F &f, O &&o) { + return bind_first(f, std::forward(o), &F::operator()); + } + } // namespace detail +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index c74b86b9..ba1de5ad 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -1,398 +1,333 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BOOTSTRAP_HPP_ #define CHAISCRIPT_BOOTSTRAP_HPP_ #include "../utility/utility.hpp" #include "register_function.hpp" -namespace chaiscript -{ - /// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types - namespace bootstrap - { - template::value>::type > - void array(const std::string &type, Module& m) - { - typedef typename std::remove_extent::type ReturnType; - m.add(user_type(), type); - m.add(fun( - [](T& t, size_t index)->ReturnType &{ - constexpr auto extent = std::extent::value; - if (extent > 0 && index >= extent) { - throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); - } else { - return t[index]; - } - } - ), "[]" - ); +/// \brief Classes and functions useful for bootstrapping of ChaiScript and adding of new types +namespace chaiscript::bootstrap { + template::value>::type> + void array(const std::string &type, Module &m) { + using ReturnType = typename std::remove_extent::type; - m.add(fun( - [](const T &t, size_t index)->const ReturnType &{ - constexpr auto extent = std::extent::value; - if (extent > 0 && index >= extent) { - throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + std::to_string(extent)); - } else { - return t[index]; - } - } - ), "[]" - ); + m.add(user_type(), type); + m.add(fun([](T &t, size_t index) -> ReturnType & { + constexpr const auto extent = std::extent::value; + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + + std::to_string(extent)); + } else { + return t[index]; + } + }), + "[]"); - m.add(fun( - [](const T &) { - constexpr auto extent = std::extent::value; - return extent; - }), "size"); - } + m.add(fun([](const T &t, size_t index) -> const ReturnType & { + constexpr const auto extent = std::extent::value; + if (extent > 0 && index >= extent) { + throw std::range_error("Array index out of range. Received: " + std::to_string(index) + " expected < " + + std::to_string(extent)); + } else { + return t[index]; + } + }), + "[]"); - /// \brief Adds a copy constructor for the given type to the given Model - /// \param[in] type The name of the type. The copy constructor will be named "type". - /// \param[in,out] m The Module to add the copy constructor to - /// \tparam T The type to add a copy constructor for - /// \returns The passed in Module - template - void copy_constructor(const std::string &type, Module& m) - { - m.add(constructor(), type); - } + m.add(fun([](const T &) { return std::extent::value; }), "size"); + } - /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users. - /// \tparam T Type to create comparison operators for - /// \param[in,out] m module to add comparison operators to - /// \returns the passed in Module. - template - void opers_comparison(Module& m) - { - operators::equal(m); - operators::greater_than(m); - operators::greater_than_equal(m); - operators::less_than(m); - operators::less_than_equal(m); - operators::not_equal(m); - } + /// \brief Adds a copy constructor for the given type to the given Model + /// \param[in] type The name of the type. The copy constructor will be named "type". + /// \param[in,out] m The Module to add the copy constructor to + /// \tparam T The type to add a copy constructor for + /// \returns The passed in Module + template + void copy_constructor(const std::string &type, Module &m) { + m.add(constructor(), type); + } + /// \brief Add all comparison operators for the templated type. Used during bootstrap, also available to users. + /// \tparam T Type to create comparison operators for + /// \param[in,out] m module to add comparison operators to + /// \returns the passed in Module. + template + void opers_comparison(Module &m) { + operators::equal(m); + operators::greater_than(m); + operators::greater_than_equal(m); + operators::less_than(m); + operators::less_than_equal(m); + operators::not_equal(m); + } + /// \brief Adds default and copy constructors for the given type + /// \param[in] type The name of the type to add the constructors for. + /// \param[in,out] m The Module to add the basic constructors to + /// \tparam T Type to generate basic constructors for + /// \returns The passed in Module + /// \sa copy_constructor + /// \sa constructor + template + void basic_constructors(const std::string &type, Module &m) { + m.add(constructor(), type); + copy_constructor(type, m); + } - /// \brief Adds default and copy constructors for the given type - /// \param[in] type The name of the type to add the constructors for. - /// \param[in,out] m The Module to add the basic constructors to - /// \tparam T Type to generate basic constructors for - /// \returns The passed in Module - /// \sa copy_constructor - /// \sa constructor - template - void basic_constructors(const std::string &type, Module& m) - { - m.add(constructor(), type); - copy_constructor(type, m); - } + /// \brief Adds a constructor for a POD type + /// \tparam T The type to add the constructor for + /// \param[in] type The name of the type + /// \param[in,out] m The Module to add the constructor to + template + void construct_pod(const std::string &type, Module &m) { + m.add(fun([](const Boxed_Number &bn) { return bn.get_as(); }), type); + } - /// \brief Adds a constructor for a POD type - /// \tparam T The type to add the constructor for - /// \param[in] type The name of the type - /// \param[in,out] m The Module to add the constructor to - template - void construct_pod(const std::string &type, Module& m) - { - m.add(fun([](const Boxed_Number &bn){ return bn.get_as(); }), type); - } - - - /// Internal function for converting from a string to a value - /// uses ostream operator >> to perform the conversion - template - auto parse_string(const std::string &i) - -> typename std::enable_if< - !std::is_same::value - && !std::is_same::value - && !std::is_same::value, - Input>::type - { + /// Internal function for converting from a string to a value + /// uses ostream operator >> to perform the conversion + template + Input parse_string(const std::string &i) { + if constexpr (!std::is_same::value && !std::is_same::value && !std::is_same::value) { std::stringstream ss(i); Input t; ss >> t; return t; - } - - template - auto parse_string(const std::string &) - -> typename std::enable_if< - std::is_same::value - || std::is_same::value - || std::is_same::value, - Input>::type - { + } else { throw std::runtime_error("Parsing of wide characters is not yet supported"); } + } + /// Add all common functions for a POD type. All operators, and + /// common conversions + template + void bootstrap_pod_type(const std::string &name, Module &m) { + m.add(user_type(), name); + m.add(constructor(), name); + construct_pod(name, m); - /// Add all common functions for a POD type. All operators, and - /// common conversions - template - void bootstrap_pod_type(const std::string &name, Module& m) - { - m.add(user_type(), name); - m.add(constructor(), name); - construct_pod(name, m); + m.add(fun(&parse_string), "to_" + name); + m.add(fun([](const T t) { return t; }), "to_" + name); + } - m.add(fun(&parse_string), "to_" + name); - m.add(fun([](const T t){ return t; }), "to_" + name); + /// "clone" function for a shared_ptr type. This is used in the case + /// where you do not want to make a deep copy of an object during cloning + /// but want to instead maintain the shared_ptr. It is needed internally + /// for handling of Proxy_Function object (that is, + /// function variables. + template + auto shared_ptr_clone(const std::shared_ptr &p) { + return p; + } + + /// Specific version of shared_ptr_clone just for Proxy_Functions + template + std::shared_ptr> shared_ptr_unconst_clone(const std::shared_ptr> &p) { + return std::const_pointer_cast::type>(p); + } + + /// Assignment function for shared_ptr objects, does not perform a copy of the + /// object pointed to, instead maintains the shared_ptr concept. + /// Similar to shared_ptr_clone. Used for Proxy_Function. + template + Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr &rhs) { + if (lhs.is_undef() || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info::get()))) { + lhs.assign(Boxed_Value(rhs)); + return lhs; + } else { + throw exception::bad_boxed_cast("type mismatch in pointer assignment"); } + } - - /// "clone" function for a shared_ptr type. This is used in the case - /// where you do not want to make a deep copy of an object during cloning - /// but want to instead maintain the shared_ptr. It is needed internally - /// for handling of Proxy_Function object (that is, - /// function variables. - template - auto shared_ptr_clone(const std::shared_ptr &p) - { - return p; - } - - /// Specific version of shared_ptr_clone just for Proxy_Functions - template - std::shared_ptr::type> shared_ptr_unconst_clone(const std::shared_ptr::type> &p) - { - return std::const_pointer_cast::type>(p); - } - - - - /// Assignment function for shared_ptr objects, does not perform a copy of the - /// object pointed to, instead maintains the shared_ptr concept. - /// Similar to shared_ptr_clone. Used for Proxy_Function. - template - Boxed_Value ptr_assign(Boxed_Value lhs, const std::shared_ptr &rhs) - { - if (lhs.is_undef() - || (!lhs.get_type_info().is_const() && lhs.get_type_info().bare_equal(chaiscript::detail::Get_Type_Info::get()))) - { - lhs.assign(Boxed_Value(rhs)); - return lhs; + /// Class consisting of only static functions. All default bootstrapping occurs + /// from this class. + class Bootstrap { + private: + /// Function allowing for assignment of an unknown type to any other value + static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) { + if (lhs.is_undef()) { + return (lhs.assign(rhs)); } else { - throw exception::bad_boxed_cast("type mismatch in pointer assignment"); + throw exception::bad_boxed_cast("boxed_value has a set type already"); } } - /// Class consisting of only static functions. All default bootstrapping occurs - /// from this class. - class Bootstrap - { - private: - /// Function allowing for assignment of an unknown type to any other value - static Boxed_Value unknown_assign(Boxed_Value lhs, Boxed_Value rhs) - { - if (lhs.is_undef()) - { - return (lhs.assign(rhs)); - } else { - throw exception::bad_boxed_cast("boxed_value has a set type already"); - } + static void print(const std::string &s) noexcept { fwrite(s.c_str(), 1, s.size(), stdout); } + + static void println(const std::string &s) noexcept { puts(s.c_str()); } + + /// Add all arithmetic operators for PODs + static void opers_arithmetic_pod(Module &m) { + m.add(fun(&Boxed_Number::equals), "=="); + m.add(fun(&Boxed_Number::less_than), "<"); + m.add(fun(&Boxed_Number::greater_than), ">"); + m.add(fun(&Boxed_Number::greater_than_equal), ">="); + m.add(fun(&Boxed_Number::less_than_equal), "<="); + m.add(fun(&Boxed_Number::not_equal), "!="); + + m.add(fun(&Boxed_Number::pre_decrement), "--"); + m.add(fun(&Boxed_Number::pre_increment), "++"); + m.add(fun(&Boxed_Number::sum), "+"); + m.add(fun(&Boxed_Number::unary_plus), "+"); + m.add(fun(&Boxed_Number::unary_minus), "-"); + m.add(fun(&Boxed_Number::difference), "-"); + m.add(fun(&Boxed_Number::assign_bitwise_and), "&="); + m.add(fun(&Boxed_Number::assign), "="); + m.add(fun(&Boxed_Number::assign_bitwise_or), "|="); + m.add(fun(&Boxed_Number::assign_bitwise_xor), "^="); + m.add(fun(&Boxed_Number::assign_remainder), "%="); + m.add(fun(&Boxed_Number::assign_shift_left), "<<="); + m.add(fun(&Boxed_Number::assign_shift_right), ">>="); + m.add(fun(&Boxed_Number::bitwise_and), "&"); + m.add(fun(&Boxed_Number::bitwise_complement), "~"); + m.add(fun(&Boxed_Number::bitwise_xor), "^"); + m.add(fun(&Boxed_Number::bitwise_or), "|"); + m.add(fun(&Boxed_Number::assign_product), "*="); + m.add(fun(&Boxed_Number::assign_quotient), "/="); + m.add(fun(&Boxed_Number::assign_sum), "+="); + m.add(fun(&Boxed_Number::assign_difference), "-="); + m.add(fun(&Boxed_Number::quotient), "/"); + m.add(fun(&Boxed_Number::shift_left), "<<"); + m.add(fun(&Boxed_Number::product), "*"); + m.add(fun(&Boxed_Number::remainder), "%"); + m.add(fun(&Boxed_Number::shift_right), ">>"); + } + + /// Create a bound function object. The first param is the function to bind + /// the remaining parameters are the args to bind into the result + static Boxed_Value bind_function(const Function_Params ¶ms) { + if (params.empty()) { + throw exception::arity_error(0, 1); } - static void print(const std::string &s) - { - fwrite(s.c_str(), 1, s.size(), stdout); + Const_Proxy_Function f = boxed_cast(params[0]); + + if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) { + throw exception::arity_error(static_cast(params.size()), f->get_arity()); } - static void println(const std::string &s) - { - puts(s.c_str()); + return Boxed_Value(Const_Proxy_Function( + std::make_shared(std::move(f), std::vector(params.begin() + 1, params.end())))); + } + + static bool has_guard(const Const_Proxy_Function &t_pf) noexcept { + auto pf = std::dynamic_pointer_cast(t_pf); + return pf && pf->has_guard(); + } + + static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) { + const auto pf = std::dynamic_pointer_cast(t_pf); + if (pf && pf->get_guard()) { + return pf->get_guard(); + } else { + throw std::runtime_error("Function does not have a guard"); + } + } + + template + static std::vector do_return_boxed_value_vector(FunctionType f, const dispatch::Proxy_Function_Base *b) { + auto v = (b->*f)(); + + std::vector vbv; + + for (const auto &o : v) { + vbv.push_back(const_var(o)); } + return vbv; + } - /// Add all arithmetic operators for PODs - static void opers_arithmetic_pod(Module& m) - { - m.add(fun(&Boxed_Number::equals), "=="); - m.add(fun(&Boxed_Number::less_than), "<"); - m.add(fun(&Boxed_Number::greater_than), ">"); - m.add(fun(&Boxed_Number::greater_than_equal), ">="); - m.add(fun(&Boxed_Number::less_than_equal), "<="); - m.add(fun(&Boxed_Number::not_equal), "!="); + static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) noexcept { + const auto pf = std::dynamic_pointer_cast(t_pf); + return bool(pf); + } - m.add(fun(&Boxed_Number::pre_decrement), "--"); - m.add(fun(&Boxed_Number::pre_increment), "++"); - m.add(fun(&Boxed_Number::sum), "+"); - m.add(fun(&Boxed_Number::unary_plus), "+"); - m.add(fun(&Boxed_Number::unary_minus), "-"); - m.add(fun(&Boxed_Number::difference), "-"); - m.add(fun(&Boxed_Number::assign_bitwise_and), "&="); - m.add(fun(&Boxed_Number::assign), "="); - m.add(fun(&Boxed_Number::assign_bitwise_or), "|="); - m.add(fun(&Boxed_Number::assign_bitwise_xor), "^="); - m.add(fun(&Boxed_Number::assign_remainder), "%="); - m.add(fun(&Boxed_Number::assign_shift_left), "<<="); - m.add(fun(&Boxed_Number::assign_shift_right), ">>="); - m.add(fun(&Boxed_Number::bitwise_and), "&"); - m.add(fun(&Boxed_Number::bitwise_complement), "~"); - m.add(fun(&Boxed_Number::bitwise_xor), "^"); - m.add(fun(&Boxed_Number::bitwise_or), "|"); - m.add(fun(&Boxed_Number::assign_product), "*="); - m.add(fun(&Boxed_Number::assign_quotient), "/="); - m.add(fun(&Boxed_Number::assign_sum), "+="); - m.add(fun(&Boxed_Number::assign_difference), "-="); - m.add(fun(&Boxed_Number::quotient), "/"); - m.add(fun(&Boxed_Number::shift_left), "<<"); - m.add(fun(&Boxed_Number::product), "*"); - m.add(fun(&Boxed_Number::remainder), "%"); - m.add(fun(&Boxed_Number::shift_right), ">>"); - } - - /// Create a bound function object. The first param is the function to bind - /// the remaining parameters are the args to bind into the result - static Boxed_Value bind_function(const std::vector ¶ms) - { - if (params.empty()) { - throw exception::arity_error(0, 1); - } - - Const_Proxy_Function f = boxed_cast(params[0]); - - if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) - { - throw exception::arity_error(static_cast(params.size()), f->get_arity()); - } - - return Boxed_Value(Const_Proxy_Function(std::make_shared(std::move(f), - std::vector(params.begin() + 1, params.end())))); + static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) { + const auto pf = std::dynamic_pointer_cast(t_pf); + if (pf) { + return pf->get_parse_tree(); + } else { + throw std::runtime_error("Function does not have a parse tree"); } + } + template + static auto return_boxed_value_vector(const Function &f) { + return [f](const dispatch::Proxy_Function_Base *b) { return do_return_boxed_value_vector(f, b); }; + } - static bool has_guard(const Const_Proxy_Function &t_pf) - { - auto pf = std::dynamic_pointer_cast(t_pf); - return pf && pf->get_guard(); - } + public: + /// \brief perform all common bootstrap functions for std::string, void and POD types + /// \param[in,out] m Module to add bootstrapped functions to + /// \returns passed in Module + static void bootstrap(Module &m) { + m.add(user_type(), "void"); + m.add(user_type(), "bool"); + m.add(user_type(), "Object"); + m.add(user_type(), "Number"); + m.add(user_type(), "Function"); + m.add(user_type(), "Assignable_Function"); + m.add(user_type(), "exception"); - static Const_Proxy_Function get_guard(const Const_Proxy_Function &t_pf) - { - const auto pf = std::dynamic_pointer_cast(t_pf); - if (pf && pf->get_guard()) - { - return pf->get_guard(); - } else { - throw std::runtime_error("Function does not have a guard"); - } - } + m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); + m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); - template - static std::vector do_return_boxed_value_vector(FunctionType f, - const dispatch::Proxy_Function_Base *b) - { - auto v = (b->*f)(); - - std::vector vbv; + m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types"); + m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions"); - for (const auto &o: v) - { - vbv.push_back(const_var(o)); - } + m.add(fun([](const std::exception &e) { return std::string(e.what()); }), "what"); - return vbv; - } + m.add(user_type(), "out_of_range"); + m.add(user_type(), "logic_error"); + m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); + m.add(user_type(), "runtime_error"); + m.add(chaiscript::base_class()); - static bool has_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) - { - const auto pf = std::dynamic_pointer_cast(t_pf); - return bool(pf); - } + m.add(constructor(), "runtime_error"); - static const chaiscript::AST_Node &get_parse_tree(const chaiscript::Const_Proxy_Function &t_pf) - { - const auto pf = std::dynamic_pointer_cast(t_pf); - if (pf) - { - return pf->get_parse_tree(); - } else { - throw std::runtime_error("Function does not have a parse tree"); - } - } + m.add(user_type(), "Dynamic_Object"); + m.add(constructor(), "Dynamic_Object"); + m.add(constructor(), "Dynamic_Object"); + m.add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); + m.add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); + m.add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); + m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); + m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_attr"); - template - static auto return_boxed_value_vector(const Function &f) - { - return [f](const dispatch::Proxy_Function_Base *b) { - return do_return_boxed_value_vector(f, b); - }; - } + m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); + m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), + "get_attr"); + m.add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), + "method_missing"); + m.add(fun(static_cast( + &dispatch::Dynamic_Object::method_missing)), + "method_missing"); - public: - /// \brief perform all common bootstrap functions for std::string, void and POD types - /// \param[in,out] m Module to add bootstrapped functions to - /// \returns passed in Module - static void bootstrap(Module& m) - { - m.add(user_type(), "void"); - m.add(user_type(), "bool"); - m.add(user_type(), "Object"); - m.add(user_type(), "Number"); - m.add(user_type(), "Function"); - m.add(user_type(), "Assignable_Function"); - m.add(user_type(), "exception"); + m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); + m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), + "[]"); - m.add(fun(&dispatch::Proxy_Function_Base::get_arity), "get_arity"); - m.add(fun(&dispatch::Proxy_Function_Base::operator==), "=="); - - - m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_param_types)), "get_param_types"); - m.add(fun(return_boxed_value_vector(&dispatch::Proxy_Function_Base::get_contained_functions)), "get_contained_functions"); - - m.add(fun([](const std::exception &e){ return std::string(e.what()); }), "what"); - - m.add(user_type(), "out_of_range"); - m.add(user_type(), "logic_error"); - m.add(chaiscript::base_class()); - m.add(chaiscript::base_class()); - m.add(chaiscript::base_class()); - - m.add(user_type(), "runtime_error"); - m.add(chaiscript::base_class()); - - m.add(constructor(), "runtime_error"); - - m.add(user_type(), "Dynamic_Object"); - m.add(constructor(), "Dynamic_Object"); - m.add(constructor(), "Dynamic_Object"); - m.add(fun(&dispatch::Dynamic_Object::get_type_name), "get_type_name"); - m.add(fun(&dispatch::Dynamic_Object::get_attrs), "get_attrs"); - m.add(fun(&dispatch::Dynamic_Object::set_explicit), "set_explicit"); - m.add(fun(&dispatch::Dynamic_Object::is_explicit), "is_explicit"); - m.add(fun(&dispatch::Dynamic_Object::has_attr), "has_attr"); - - m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); - m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "get_attr"); - - m.add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); - m.add(fun(static_cast(&dispatch::Dynamic_Object::method_missing)), "method_missing"); - - m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); - m.add(fun(static_cast(&dispatch::Dynamic_Object::get_attr)), "[]"); - - m.eval(R"chaiscript( - def Dynamic_Object::clone() { - auto &new_o = Dynamic_Object(this.get_type_name()); - for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); - new_o; + m.eval(R"chaiscript( + def Dynamic_Object::clone() { + auto &new_o = Dynamic_Object(this.get_type_name()); + for_each(this.get_attrs(), fun[new_o](x) { new_o.get_attr(x.first) = x.second; } ); + new_o; } def `=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() { - for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } ); + for_each(rhs.get_attrs(), fun[lhs](x) { lhs.get_attr(x.first) = clone(x.second); } ); } def `!=`(Dynamic_Object lhs, Dynamic_Object rhs) : lhs.get_type_name() == rhs.get_type_name() @@ -420,173 +355,157 @@ namespace chaiscript } )chaiscript"); - m.add(fun(&has_guard), "has_guard"); - m.add(fun(&get_guard), "get_guard"); + m.add(fun(&has_guard), "has_guard"); + m.add(fun(&get_guard), "get_guard"); - m.add(fun(&Boxed_Value::is_undef), "is_var_undef"); - m.add(fun(&Boxed_Value::is_null), "is_var_null"); - m.add(fun(&Boxed_Value::is_const), "is_var_const"); - m.add(fun(&Boxed_Value::is_ref), "is_var_reference"); - m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); - m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); - m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); - m.add(fun(&Boxed_Value::is_type), "is_type"); - m.add(fun(&Boxed_Value::get_attr), "get_var_attr"); - m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); - m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); + m.add(fun(&Boxed_Value::is_undef), "is_var_undef"); + m.add(fun(&Boxed_Value::is_null), "is_var_null"); + m.add(fun(&Boxed_Value::is_const), "is_var_const"); + m.add(fun(&Boxed_Value::is_ref), "is_var_reference"); + m.add(fun(&Boxed_Value::is_pointer), "is_var_pointer"); + m.add(fun(&Boxed_Value::is_return_value), "is_var_return_value"); + m.add(fun(&Boxed_Value::reset_return_value), "reset_var_return_value"); + m.add(fun(&Boxed_Value::is_type), "is_type"); + m.add(fun(&Boxed_Value::get_attr), "get_var_attr"); + m.add(fun(&Boxed_Value::copy_attrs), "copy_var_attrs"); + m.add(fun(&Boxed_Value::clone_attrs), "clone_var_attrs"); - m.add(fun(&Boxed_Value::get_type_info), "get_type_info"); - m.add(user_type(), "Type_Info"); - m.add(constructor(), "Type_Info"); + m.add(fun(&Boxed_Value::get_type_info), "get_type_info"); + m.add(user_type(), "Type_Info"); + m.add(constructor(), "Type_Info"); + operators::equal(m); - operators::equal(m); + m.add(fun(&Type_Info::is_const), "is_type_const"); + m.add(fun(&Type_Info::is_reference), "is_type_reference"); + m.add(fun(&Type_Info::is_void), "is_type_void"); + m.add(fun(&Type_Info::is_undef), "is_type_undef"); + m.add(fun(&Type_Info::is_pointer), "is_type_pointer"); + m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); + m.add(fun(&Type_Info::name), "cpp_name"); + m.add(fun(&Type_Info::bare_name), "cpp_bare_name"); + m.add(fun(&Type_Info::bare_equal), "bare_equal"); - m.add(fun(&Type_Info::is_const), "is_type_const"); - m.add(fun(&Type_Info::is_reference), "is_type_reference"); - m.add(fun(&Type_Info::is_void), "is_type_void"); - m.add(fun(&Type_Info::is_undef), "is_type_undef"); - m.add(fun(&Type_Info::is_pointer), "is_type_pointer"); - m.add(fun(&Type_Info::is_arithmetic), "is_type_arithmetic"); - m.add(fun(&Type_Info::name), "cpp_name"); - m.add(fun(&Type_Info::bare_name), "cpp_bare_name"); - m.add(fun(&Type_Info::bare_equal), "bare_equal"); + basic_constructors("bool", m); + operators::assign(m); + operators::equal(m); + operators::not_equal(m); + m.add(fun([](const std::string &s) { return s; }), "to_string"); + m.add(fun([](const bool b) { return std::string(b ? "true" : "false"); }), "to_string"); + m.add(fun(&unknown_assign), "="); + m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); - basic_constructors("bool", m); - operators::assign(m); - operators::equal(m); - operators::not_equal(m); + m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); + m.add(fun(&Boxed_Number::to_string), "to_string"); - m.add(fun([](const std::string &s) { return s; }), "to_string"); - m.add(fun([](const bool b) { return std::string(b?"true":"false"); }), "to_string"); - m.add(fun(&unknown_assign), "="); - m.add(fun([](const Boxed_Value &bv) { throw bv; }), "throw"); + bootstrap_pod_type("double", m); + bootstrap_pod_type("long_double", m); + bootstrap_pod_type("float", m); + bootstrap_pod_type("int", m); + bootstrap_pod_type("long", m); + bootstrap_pod_type("unsigned_int", m); + bootstrap_pod_type("unsigned_long", m); + bootstrap_pod_type("long_long", m); + bootstrap_pod_type("unsigned_long_long", m); + bootstrap_pod_type("size_t", m); + bootstrap_pod_type("char", m); + bootstrap_pod_type("wchar_t", m); + bootstrap_pod_type("char16_t", m); + bootstrap_pod_type("char32_t", m); + bootstrap_pod_type("int8_t", m); + bootstrap_pod_type("int16_t", m); + bootstrap_pod_type("int32_t", m); + bootstrap_pod_type("int64_t", m); + bootstrap_pod_type("uint8_t", m); + bootstrap_pod_type("uint16_t", m); + bootstrap_pod_type("uint32_t", m); + bootstrap_pod_type("uint64_t", m); - m.add(fun([](const char c) { return std::string(1, c); }), "to_string"); - m.add(fun(&Boxed_Number::to_string), "to_string"); + operators::logical_compliment(m); + opers_arithmetic_pod(m); - bootstrap_pod_type("double", m); - bootstrap_pod_type("long_double", m); - bootstrap_pod_type("float", m); - bootstrap_pod_type("int", m); - bootstrap_pod_type("long", m); - bootstrap_pod_type("unsigned_int", m); - bootstrap_pod_type("unsigned_long", m); - bootstrap_pod_type("long_long", m); - bootstrap_pod_type("unsigned_long_long", m); - bootstrap_pod_type("size_t", m); - bootstrap_pod_type("char", m); - bootstrap_pod_type("wchar_t", m); - bootstrap_pod_type("char16_t", m); - bootstrap_pod_type("char32_t", m); - bootstrap_pod_type("int8_t", m); - bootstrap_pod_type("int16_t", m); - bootstrap_pod_type("int32_t", m); - bootstrap_pod_type("int64_t", m); - bootstrap_pod_type("uint8_t", m); - bootstrap_pod_type("uint16_t", m); - bootstrap_pod_type("uint32_t", m); - bootstrap_pod_type("uint64_t", m); + m.add(fun(&Build_Info::version_major), "version_major"); + m.add(fun(&Build_Info::version_minor), "version_minor"); + m.add(fun(&Build_Info::version_patch), "version_patch"); + m.add(fun(&Build_Info::version), "version"); + m.add(fun(&Build_Info::compiler_version), "compiler_version"); + m.add(fun(&Build_Info::compiler_name), "compiler_name"); + m.add(fun(&Build_Info::compiler_id), "compiler_id"); + m.add(fun(&Build_Info::debug_build), "debug_build"); + m.add(fun(&print), "print_string"); + m.add(fun(&println), "println_string"); - operators::logical_compliment(m); + m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); - opers_arithmetic_pod(m); + m.add(fun(&shared_ptr_unconst_clone), "clone"); + m.add(fun(&ptr_assign::type>), "="); + m.add(fun(&ptr_assign::type>), "="); + m.add(chaiscript::base_class()); + m.add(fun([](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr &t_rhs) { + t_lhs.assign(t_rhs); + }), + "="); + m.add(fun(&Boxed_Value::type_match), "type_match"); - m.add(fun(&Build_Info::version_major), "version_major"); - m.add(fun(&Build_Info::version_minor), "version_minor"); - m.add(fun(&Build_Info::version_patch), "version_patch"); - m.add(fun(&Build_Info::version), "version"); - m.add(fun(&Build_Info::compiler_version), "compiler_version"); - m.add(fun(&Build_Info::compiler_name), "compiler_name"); - m.add(fun(&Build_Info::compiler_id), "compiler_id"); - m.add(fun(&Build_Info::debug_build), "debug_build"); + m.add(chaiscript::fun(&has_parse_tree), "has_parse_tree"); + m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); + m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); - m.add(fun(&print), "print_string"); - m.add(fun(&println), "println_string"); + m.add(chaiscript::user_type(), "arithmetic_error"); + m.add(chaiscript::base_class()); + m.add(chaiscript::base_class()); - m.add(dispatch::make_dynamic_proxy_function(&bind_function), "bind"); + // chaiscript::bootstrap::standard_library::vector_type > + // >("AST_NodeVector", m); - m.add(fun(&shared_ptr_unconst_clone), "clone"); - m.add(fun(&ptr_assign::type>), "="); - m.add(fun(&ptr_assign::type>), "="); - m.add(chaiscript::base_class()); - m.add(fun( - [](dispatch::Assignable_Proxy_Function &t_lhs, const std::shared_ptr &t_rhs) { - t_lhs.assign(t_rhs); - } - ), "=" - ); + chaiscript::utility::add_class(m, + "eval_error", + {}, + {{fun(&chaiscript::exception::eval_error::reason), "reason"}, + {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, + {fun([](const chaiscript::exception::eval_error &t_eval_error) { + std::vector retval; + std::transform(t_eval_error.call_stack.begin(), + t_eval_error.call_stack.end(), + std::back_inserter(retval), + &chaiscript::var); + return retval; + }), + "call_stack"}}); - m.add(fun(&Boxed_Value::type_match), "type_match"); + chaiscript::utility::add_class(m, + "File_Position", + {constructor(), constructor()}, + {{fun(&File_Position::line), "line"}, + {fun(&File_Position::column), "column"}}); - - m.add(chaiscript::fun(&has_parse_tree), "has_parse_tree"); - m.add(chaiscript::fun(&get_parse_tree), "get_parse_tree"); - - m.add(chaiscript::base_class()); - m.add(chaiscript::base_class()); - - m.add(chaiscript::user_type(), "arithmetic_error"); - m.add(chaiscript::base_class()); - m.add(chaiscript::base_class()); - - -// chaiscript::bootstrap::standard_library::vector_type > >("AST_NodeVector", m); - - - chaiscript::utility::add_class(m, - "eval_error", - { }, - { {fun(&chaiscript::exception::eval_error::reason), "reason"}, - {fun(&chaiscript::exception::eval_error::pretty_print), "pretty_print"}, - {fun([](const chaiscript::exception::eval_error &t_eval_error) { - std::vector retval; - std::transform(t_eval_error.call_stack.begin(), t_eval_error.call_stack.end(), - std::back_inserter(retval), - &chaiscript::var); - return retval; - }), "call_stack"} } - ); - - - chaiscript::utility::add_class(m, - "File_Position", - { constructor(), - constructor() }, - { {fun(&File_Position::line), "line"}, - {fun(&File_Position::column), "column"} } - ); - - - chaiscript::utility::add_class(m, - "AST_Node", - { }, - { {fun(&AST_Node::text), "text"}, - {fun(&AST_Node::identifier), "identifier"}, - {fun(&AST_Node::filename), "filename"}, - {fun(&AST_Node::start), "start"}, - {fun(&AST_Node::end), "end"}, - {fun(&AST_Node::to_string), "to_string"}, - {fun([](const chaiscript::AST_Node &t_node) -> std::vector { - std::vector retval; - const auto children = t_node.get_children(); - std::transform(children.begin(), children.end(), - std::back_inserter(retval), - &chaiscript::var &>); - return retval; - }), "children"} - } - ); - - } - }; - } -} + chaiscript::utility::add_class(m, + "AST_Node", + {}, + {{fun(&AST_Node::text), "text"}, + {fun(&AST_Node::identifier), "identifier"}, + {fun(&AST_Node::filename), "filename"}, + {fun(&AST_Node::start), "start"}, + {fun(&AST_Node::end), "end"}, + {fun(&AST_Node::to_string), "to_string"}, + {fun([](const chaiscript::AST_Node &t_node) -> std::vector { + std::vector retval; + const auto children = t_node.get_children(); + std::transform(children.begin(), + children.end(), + std::back_inserter(retval), + &chaiscript::var &>); + return retval; + }), + "children"}}); + } + }; +} // namespace chaiscript::bootstrap #endif - diff --git a/include/chaiscript/dispatchkit/bootstrap_stl.hpp b/include/chaiscript/dispatchkit/bootstrap_stl.hpp index bc3c0869..7592c511 100644 --- a/include/chaiscript/dispatchkit/bootstrap_stl.hpp +++ b/include/chaiscript/dispatchkit/bootstrap_stl.hpp @@ -1,19 +1,17 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /// \file /// This file contains utility functions for registration of STL container /// classes. The methodology used is based on the SGI STL concepts. /// http://www.sgi.com/tech/stl/table_of_contents.html - #ifndef CHAISCRIPT_BOOTSTRAP_STL_HPP_ #define CHAISCRIPT_BOOTSTRAP_STL_HPP_ @@ -31,515 +29,353 @@ #include "register_function.hpp" #include "type_info.hpp" -namespace chaiscript -{ - namespace bootstrap - { - namespace standard_library - { +namespace chaiscript::bootstrap::standard_library { + /// Bidir_Range, based on the D concept of ranges. + /// \todo Update the Range code to base its capabilities on + /// the user_typetraits of the iterator passed in + template + struct Bidir_Range { + using container_type = Container; - /// Bidir_Range, based on the D concept of ranges. - /// \todo Update the Range code to base its capabilities on - /// the user_typetraits of the iterator passed in - template - struct Bidir_Range - { - typedef Container container_type; + constexpr Bidir_Range(Container &c) + : m_begin(c.begin()) + , m_end(c.end()) { + } - Bidir_Range(Container &c) - : m_begin(c.begin()), m_end(c.end()) - { - } + constexpr bool empty() const noexcept { return m_begin == m_end; } - bool empty() const - { - return m_begin == m_end; - } + constexpr void pop_front() { + if (empty()) { + throw std::range_error("Range empty"); + } + ++m_begin; + } - void pop_front() - { - if (empty()) - { - throw std::range_error("Range empty"); - } - ++m_begin; - } + constexpr void pop_back() { + if (empty()) { + throw std::range_error("Range empty"); + } + --m_end; + } - void pop_back() - { - if (empty()) - { - throw std::range_error("Range empty"); - } - --m_end; - } + constexpr decltype(auto) front() const { + if (empty()) { + throw std::range_error("Range empty"); + } + return (*m_begin); + } - decltype(auto) front() const - { - if (empty()) - { - throw std::range_error("Range empty"); - } - return (*m_begin); - } + constexpr decltype(auto) back() const { + if (empty()) { + throw std::range_error("Range empty"); + } + auto pos = m_end; + --pos; + return (*(pos)); + } - decltype(auto) back() const - { - if (empty()) - { - throw std::range_error("Range empty"); - } - auto pos = m_end; - --pos; - return (*(pos)); - } + IterType m_begin; + IterType m_end; + }; - IterType m_begin; - IterType m_end; - }; + namespace detail { + template + size_t count(const T &t_target, const typename T::key_type &t_key) { + return t_target.count(t_key); + } - namespace detail { + template + void insert(T &t_target, const T &t_other) { + t_target.insert(t_other.begin(), t_other.end()); + } - template - size_t count(const T &t_target, const typename T::key_type &t_key) - { - return t_target.count(t_key); - } + template + void insert_ref(T &t_target, const typename T::value_type &t_val) { + t_target.insert(t_val); + } - template - void insert(T &t_target, const T &t_other) - { - t_target.insert(t_other.begin(), t_other.end()); - } + /// Add Bidir_Range support for the given ContainerType + template + void input_range_type_impl(const std::string &type, Module &m) { + m.add(user_type(), type + "_Range"); - template - void insert_ref(T &t_target, const typename T::value_type &t_val) - { - t_target.insert(t_val); - } + copy_constructor(type + "_Range", m); + m.add(constructor(), "range_internal"); + m.add(fun(&Bidir_Type::empty), "empty"); + m.add(fun(&Bidir_Type::pop_front), "pop_front"); + m.add(fun(&Bidir_Type::front), "front"); + m.add(fun(&Bidir_Type::pop_back), "pop_back"); + m.add(fun(&Bidir_Type::back), "back"); + } - /// Add Bidir_Range support for the given ContainerType - template - void input_range_type_impl(const std::string &type, Module& m) - { - m.add(user_type(), type + "_Range"); + /// Algorithm for inserting at a specific position into a container + template + void insert_at(Type &container, int pos, const typename Type::value_type &v) { + auto itr = container.begin(); + auto end = container.end(); - copy_constructor(type + "_Range", m); - - m.add(constructor(), "range_internal"); - - m.add(fun(&Bidir_Type::empty), "empty"); - m.add(fun(&Bidir_Type::pop_front), "pop_front"); - m.add(fun(&Bidir_Type::front), "front"); - m.add(fun(&Bidir_Type::pop_back), "pop_back"); - m.add(fun(&Bidir_Type::back), "back"); - } - - - /// Algorithm for inserting at a specific position into a container - template - void insert_at(Type &container, int pos, const typename Type::value_type &v) - { - auto itr = container.begin(); - auto end = container.end(); - - if (pos < 0 || std::distance(itr, end) < pos) - { - throw std::range_error("Cannot insert past end of range"); - } - - std::advance(itr, pos); - container.insert(itr, v); - } - - - /// Algorithm for erasing a specific position from a container - template - void erase_at(Type &container, int pos) - { - auto itr = container.begin(); - auto end = container.end(); - - if (pos < 0 || std::distance(itr, end) < (pos-1)) - { - throw std::range_error("Cannot erase past end of range"); - } - - std::advance(itr, pos); - container.erase(itr); - } + if (pos < 0 || std::distance(itr, end) < pos) { + throw std::range_error("Cannot insert past end of range"); } - template - void input_range_type(const std::string &type, Module& m) - { - detail::input_range_type_impl >(type,m); - detail::input_range_type_impl >("Const_" + type,m); - } - template - ModulePtr input_range_type(const std::string &type) - { - auto m = std::make_shared(); - input_range_type(type, *m); - return m; - } + std::advance(itr, pos); + container.insert(itr, v); + } + /// Algorithm for erasing a specific position from a container + template + void erase_at(Type &container, int pos) { + auto itr = container.begin(); + auto end = container.end(); - /// Add random_access_container concept to the given ContainerType - /// http://www.sgi.com/tech/stl/RandomAccessContainer.html - template - void random_access_container_type(const std::string &/*type*/, Module& m) - { - //In the interest of runtime safety for the m, we prefer the at() method for [] access, - //to throw an exception in an out of bounds condition. - m.add( - fun( - [](ContainerType &c, int index) -> typename ContainerType::reference { - /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions - /// during dispatch. reevaluate - return c.at(static_cast(index)); - }), "[]"); - - m.add( - fun( - [](const ContainerType &c, int index) -> typename ContainerType::const_reference { - /// \todo we are prefering to keep the key as 'int' to avoid runtime conversions - /// during dispatch. reevaluate - return c.at(static_cast(index)); - }), "[]"); - } - template - ModulePtr random_access_container_type(const std::string &type) - { - auto m = std::make_shared(); - random_access_container_type(type, *m); - return m; - } - - - - /// Add assignable concept to the given ContainerType - /// http://www.sgi.com/tech/stl/Assignable.html - template - void assignable_type(const std::string &type, Module& m) - { - copy_constructor(type, m); - operators::assign(m); - } - template - ModulePtr assignable_type(const std::string &type) - { - auto m = std::make_shared(); - assignable_type(type, *m); - return m; - } - - - /// Add container resize concept to the given ContainerType - /// http://www.cplusplus.com/reference/stl/ - template - void resizable_type(const std::string &/*type*/, Module& m) - { - m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type& val) { return a->resize(n, val); } ), "resize"); - m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); } ), "resize"); - } - template - ModulePtr resizable_type(const std::string &type) - { - auto m = std::make_shared(); - resizable_type(type, *m); - return m; - } - - - /// Add container reserve concept to the given ContainerType - /// http://www.cplusplus.com/reference/stl/ - template - void reservable_type(const std::string &/*type*/, Module& m) - { - m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); } ), "reserve"); - m.add(fun([](const ContainerType *a) { return a->capacity(); } ), "capacity"); - } - template - ModulePtr reservable_type(const std::string &type) - { - auto m = std::make_shared(); - reservable_type(type, *m); - return m; - } - - - /// Add container concept to the given ContainerType - /// http://www.sgi.com/tech/stl/Container.html - template - void container_type(const std::string &/*type*/, Module& m) - { - m.add(fun([](const ContainerType *a) { return a->size(); } ), "size"); - m.add(fun([](const ContainerType *a) { return a->empty(); } ), "empty"); - m.add(fun([](ContainerType *a) { a->clear(); } ), "clear"); - } - template - ModulePtr container_type(const std::string& type) - { - auto m = std::make_shared(); - container_type(type, *m); - return m; + if (pos < 0 || std::distance(itr, end) < (pos - 1)) { + throw std::range_error("Cannot erase past end of range"); } + std::advance(itr, pos); + container.erase(itr); + } + } // namespace detail - /// Add default constructable concept to the given Type - /// http://www.sgi.com/tech/stl/DefaultConstructible.html - template - void default_constructible_type(const std::string &type, Module& m) - { - m.add(constructor(), type); - } - template - ModulePtr default_constructible_type(const std::string& type) - { - auto m = std::make_shared(); - default_constructible_type(type, *m); - return m; - } + template + void input_range_type(const std::string &type, Module &m) { + detail::input_range_type_impl>(type, m); + detail::input_range_type_impl>("Const_" + type, m); + } + /// Add random_access_container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/RandomAccessContainer.html + template + void random_access_container_type(const std::string & /*type*/, Module &m) { + // In the interest of runtime safety for the m, we prefer the at() method for [] access, + // to throw an exception in an out of bounds condition. + m.add(fun([](ContainerType &c, int index) -> typename ContainerType::reference { + /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions + /// during dispatch. reevaluate + return c.at(static_cast(index)); + }), + "[]"); + m.add(fun([](const ContainerType &c, int index) -> typename ContainerType::const_reference { + /// \todo we are preferring to keep the key as 'int' to avoid runtime conversions + /// during dispatch. reevaluate + return c.at(static_cast(index)); + }), + "[]"); + } - /// Add sequence concept to the given ContainerType - /// http://www.sgi.com/tech/stl/Sequence.html - template - void sequence_type(const std::string &/*type*/, Module& m) - { - m.add(fun(&detail::insert_at), - []()->std::string{ - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - return "insert_ref_at"; - } else { - return "insert_at"; - } - }()); + /// Add assignable concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Assignable.html + template + void assignable_type(const std::string &type, Module &m) { + copy_constructor(type, m); + operators::assign(m); + } - m.add(fun(&detail::erase_at), "erase_at"); - } - template - ModulePtr sequence_type(const std::string &type) - { - auto m = std::make_shared(); - sequence_type(type, *m); - return m; - } + /// Add container resize concept to the given ContainerType + /// http://www.cplusplus.com/reference/stl/ + template + void resizable_type(const std::string & /*type*/, Module &m) { + m.add(fun([](ContainerType *a, typename ContainerType::size_type n, const typename ContainerType::value_type &val) { + return a->resize(n, val); + }), + "resize"); + m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->resize(n); }), "resize"); + } - /// Add back insertion sequence concept to the given ContainerType - /// http://www.sgi.com/tech/stl/BackInsertionSequence.html - template - void back_insertion_sequence_type(const std::string &type, Module& m) - { - m.add(fun([](ContainerType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.back()); - } - } - ) - , "back"); - m.add(fun([](const ContainerType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.back()); - } - } - ) - , "back"); + /// Add container reserve concept to the given ContainerType + /// http://www.cplusplus.com/reference/stl/ + template + void reservable_type(const std::string & /*type*/, Module &m) { + m.add(fun([](ContainerType *a, typename ContainerType::size_type n) { return a->reserve(n); }), "reserve"); + m.add(fun([](const ContainerType *a) { return a->capacity(); }), "capacity"); + } + /// Add container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Container.html + template + void container_type(const std::string & /*type*/, Module &m) { + m.add(fun([](const ContainerType *a) { return a->size(); }), "size"); + m.add(fun([](const ContainerType *a) { return a->empty(); }), "empty"); + m.add(fun([](ContainerType *a) { a->clear(); }), "clear"); + } - typedef void (ContainerType::*push_back)(const typename ContainerType::value_type &); - m.add(fun(static_cast(&ContainerType::push_back)), - [&]()->std::string{ - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - m.eval( - "# Pushes the second value onto the container while making a clone of the value\n" - "def push_back(" + type + " container, x)\n" - "{ \n" - " if (x.is_var_return_value()) {\n" - " x.reset_var_return_value() \n" - " container.push_back_ref(x) \n" - " } else { \n" - " container.push_back_ref(clone(x)); \n" - " }\n" - "} \n" - ); + /// Add default constructable concept to the given Type + /// http://www.sgi.com/tech/stl/DefaultConstructible.html + template + void default_constructible_type(const std::string &type, Module &m) { + m.add(constructor(), type); + } - return "push_back_ref"; - } else { - return "push_back"; - } - }()); + /// Add sequence concept to the given ContainerType + /// http://www.sgi.com/tech/stl/Sequence.html + template + void sequence_type(const std::string & /*type*/, Module &m) { + m.add(fun(&detail::insert_at), []() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + return "insert_ref_at"; + } else { + return "insert_at"; + } + }()); - m.add(fun(&ContainerType::pop_back), "pop_back"); - } - template - ModulePtr back_insertion_sequence_type(const std::string &type) - { - auto m = std::make_shared(); - back_insertion_sequence_type(type, *m); - return m; - } + m.add(fun(&detail::erase_at), "erase_at"); + } + /// Add back insertion sequence concept to the given ContainerType + /// http://www.sgi.com/tech/stl/BackInsertionSequence.html + template + void back_insertion_sequence_type(const std::string &type, Module &m) { + m.add(fun([](ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + }), + "back"); + m.add(fun([](const ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.back()); + } + }), + "back"); + using push_back = void (ContainerType::*)(const typename ContainerType::value_type &); + m.add(fun(static_cast(&ContainerType::push_back)), [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m.eval("# Pushes the second value onto the container while making a clone of the value\n" + "def push_back(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_back_ref(x) \n" + " } else { \n" + " container.push_back_ref(clone(x)); \n" + " }\n" + "} \n"); - /// Front insertion sequence - /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html - template - void front_insertion_sequence_type(const std::string &type, Module& m) - { - typedef void (ContainerType::*push_ptr)(typename ContainerType::const_reference); - typedef void (ContainerType::*pop_ptr)(); + return "push_back_ref"; + } else { + return "push_back"; + } + }()); - m.add(fun([](ContainerType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.front()); - } - } - ) - , "front"); + m.add(fun(&ContainerType::pop_back), "pop_back"); + } - m.add(fun([](const ContainerType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.front()); - } - } - ) - , "front"); + /// Front insertion sequence + /// http://www.sgi.com/tech/stl/FrontInsertionSequence.html + template + void front_insertion_sequence_type(const std::string &type, Module &m) { + using push_ptr = void (ContainerType::*)(typename ContainerType::const_reference); + using pop_ptr = void (ContainerType::*)(); + m.add(fun([](ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); - m.add(fun(static_cast(&ContainerType::push_front)), - [&]()->std::string{ - if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { - m.eval( - "# Pushes the second value onto the front of container while making a clone of the value\n" - "def push_front(" + type + " container, x)\n" - "{ \n" - " if (x.is_var_return_value()) {\n" - " x.reset_var_return_value() \n" - " container.push_front_ref(x) \n" - " } else { \n" - " container.push_front_ref(clone(x)); \n" - " }\n" - "} \n" - ); - return "push_front_ref"; - } else { - return "push_front"; - } - }()); + m.add(fun([](const ContainerType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); - m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); - } - template - ModulePtr front_insertion_sequence_type(const std::string &type) - { - auto m = std::make_shared(); - front_insertion_sequence_type(type, *m); - return m; - } + m.add(fun(static_cast(&ContainerType::push_front)), [&]() -> std::string { + if (typeid(typename ContainerType::value_type) == typeid(Boxed_Value)) { + m.eval("# Pushes the second value onto the front of container while making a clone of the value\n" + "def push_front(" + + type + + " container, x)\n" + "{ \n" + " if (x.is_var_return_value()) {\n" + " x.reset_var_return_value() \n" + " container.push_front_ref(x) \n" + " } else { \n" + " container.push_front_ref(clone(x)); \n" + " }\n" + "} \n"); + return "push_front_ref"; + } else { + return "push_front"; + } + }()); + m.add(fun(static_cast(&ContainerType::pop_front)), "pop_front"); + } - /// bootstrap a given PairType - /// http://www.sgi.com/tech/stl/pair.html - template - void pair_type(const std::string &type, Module& m) - { - m.add(user_type(), type); + /// bootstrap a given PairType + /// http://www.sgi.com/tech/stl/pair.html + template + void pair_type(const std::string &type, Module &m) { + m.add(user_type(), type); - m.add(fun(&PairType::first), "first"); - m.add(fun(&PairType::second), "second"); + m.add(fun(&PairType::first), "first"); + m.add(fun(&PairType::second), "second"); - basic_constructors(type, m); - m.add(constructor(), type); - } - template - ModulePtr pair_type(const std::string &type) - { - auto m = std::make_shared(); - pair_type(type, *m); - return m; - } + basic_constructors(type, m); + m.add(constructor(), type); + } + /// Add pair associative container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html + template + void pair_associative_container_type(const std::string &type, Module &m) { + pair_type(type + "_Pair", m); + } - /// Add pair associative container concept to the given ContainerType - /// http://www.sgi.com/tech/stl/PairAssociativeContainer.html + /// Add unique associative container concept to the given ContainerType + /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html + template + void unique_associative_container_type(const std::string & /*type*/, Module &m) { + m.add(fun(detail::count), "count"); - template - void pair_associative_container_type(const std::string &type, Module& m) - { - pair_type(type + "_Pair", m); - } - template - ModulePtr pair_associative_container_type(const std::string &type) - { - auto m = std::make_shared(); - pair_associative_container_type(type, *m); - return m; - } + using erase_ptr = size_t (ContainerType::*)(const typename ContainerType::key_type &); + m.add(fun(static_cast(&ContainerType::erase)), "erase"); - /// Add unique associative container concept to the given ContainerType - /// http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html - template - void unique_associative_container_type(const std::string &/*type*/, Module& m) - { - m.add(fun(detail::count), "count"); + m.add(fun(&detail::insert), "insert"); - typedef size_t (ContainerType::*erase_ptr)(const typename ContainerType::key_type &); + m.add(fun(&detail::insert_ref), []() -> std::string { + if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { + return "insert_ref"; + } else { + return "insert"; + } + }()); + } - m.add(fun(static_cast(&ContainerType::erase)), "erase"); + /// Add a MapType container + /// http://www.sgi.com/tech/stl/Map.html + template + void map_type(const std::string &type, Module &m) { + m.add(user_type(), type); - m.add(fun(&detail::insert), "insert"); + using elem_access = typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &); + using const_elem_access = const typename MapType::mapped_type &(MapType::*)(const typename MapType::key_type &) const; - m.add(fun(&detail::insert_ref), - []()->std::string{ - if (typeid(typename ContainerType::mapped_type) == typeid(Boxed_Value)) { - return "insert_ref"; - } else { - return "insert"; - } - }()); - } - template - ModulePtr unique_associative_container_type(const std::string &type) - { - auto m = std::make_shared(); - unique_associative_container_type(type, *m); - return m; - } + m.add(fun(static_cast(&MapType::operator[])), "[]"); + m.add(fun(static_cast(&MapType::at)), "at"); + m.add(fun(static_cast(&MapType::at)), "at"); - /// Add a MapType container - /// http://www.sgi.com/tech/stl/Map.html - template - void map_type(const std::string &type, Module& m) - { - m.add(user_type(), type); - - typedef typename MapType::mapped_type &(MapType::*elem_access)(const typename MapType::key_type &); - typedef const typename MapType::mapped_type &(MapType::*const_elem_access)(const typename MapType::key_type &) const; - - m.add(fun(static_cast(&MapType::operator[])), "[]"); - - m.add(fun(static_cast(&MapType::at)), "at"); - m.add(fun(static_cast(&MapType::at)), "at"); - - if (typeid(MapType) == typeid(std::map)) - { - m.eval(R"( + if (typeid(MapType) == typeid(std::map)) { + m.eval(R"( def Map::`==`(Map rhs) { if ( rhs.size() != this.size() ) { return false; @@ -557,93 +393,68 @@ namespace chaiscript } true; } - } )" - ); - } + } )"); + } - container_type(type, m); - default_constructible_type(type, m); - assignable_type(type, m); - unique_associative_container_type(type, m); - pair_associative_container_type(type, m); - input_range_type(type, m); - } - template - ModulePtr map_type(const std::string &type) - { - auto m = std::make_shared(); - map_type(type, *m); - return m; - } + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + unique_associative_container_type(type, m); + pair_associative_container_type(type, m); + input_range_type(type, m); + } + /// http://www.sgi.com/tech/stl/List.html + template + void list_type(const std::string &type, Module &m) { + m.add(user_type(), type); - /// http://www.sgi.com/tech/stl/List.html - template - void list_type(const std::string &type, Module& m) - { - m.add(user_type(), type); + front_insertion_sequence_type(type, m); + back_insertion_sequence_type(type, m); + sequence_type(type, m); + resizable_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + } - front_insertion_sequence_type(type, m); - back_insertion_sequence_type(type, m); - sequence_type(type, m); - resizable_type(type, m); - container_type(type, m); - default_constructible_type(type, m); - assignable_type(type, m); - input_range_type(type, m); - } - template - ModulePtr list_type(const std::string &type) - { - auto m = std::make_shared(); - list_type(type, m); - return m; - } + /// Create a vector type with associated concepts + /// http://www.sgi.com/tech/stl/Vector.html + template + void vector_type(const std::string &type, Module &m) { + m.add(user_type(), type); + m.add(fun([](VectorType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); - /// Create a vector type with associated concepts - /// http://www.sgi.com/tech/stl/Vector.html - template - void vector_type(const std::string &type, Module& m) - { - m.add(user_type(), type); + m.add(fun([](const VectorType &container) -> decltype(auto) { + if (container.empty()) { + throw std::range_error("Container empty"); + } else { + return (container.front()); + } + }), + "front"); - m.add(fun([](VectorType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.front()); - } - } - ) - , "front"); + back_insertion_sequence_type(type, m); + sequence_type(type, m); + random_access_container_type(type, m); + resizable_type(type, m); + reservable_type(type, m); + container_type(type, m); + default_constructible_type(type, m); + assignable_type(type, m); + input_range_type(type, m); - m.add(fun([](const VectorType &container)->decltype(auto){ - if (container.empty()) { - throw std::range_error("Container empty"); - } else { - return (container.front()); - } - } - ) - , "front"); - - - - - back_insertion_sequence_type(type, m); - sequence_type(type, m); - random_access_container_type(type, m); - resizable_type(type, m); - reservable_type(type, m); - container_type(type, m); - default_constructible_type(type, m); - assignable_type(type, m); - input_range_type(type, m); - - if (typeid(VectorType) == typeid(std::vector)) - { - m.eval(R"( + if (typeid(VectorType) == typeid(std::vector)) { + m.eval(R"( def Vector::`==`(Vector rhs) { if ( rhs.size() != this.size() ) { return false; @@ -661,95 +472,62 @@ namespace chaiscript } true; } - } )" - ); - } - } - template - ModulePtr vector_type(const std::string &type) - { - auto m = std::make_shared(); - vector_type(type, *m); - return m; - } - - /// Add a String container - /// http://www.sgi.com/tech/stl/basic_string.html - template - void string_type(const std::string &type, Module& m) - { - m.add(user_type(), type); - operators::addition(m); - operators::assign_sum(m); - opers_comparison(m); - random_access_container_type(type, m); - sequence_type(type, m); - default_constructible_type(type, m); - // container_type(type, m); - assignable_type(type, m); - input_range_type(type, m); - - //Special case: add push_back to string (which doesn't support other back_insertion operations - m.add(fun(&String::push_back), - []()->std::string{ - if (typeid(typename String::value_type) == typeid(Boxed_Value)) { - return "push_back_ref"; - } else { - return "push_back"; - } - }()); - - - m.add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); } ), "find"); - m.add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); } ), "rfind"); - m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); } ), "find_first_of"); - m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); } ), "find_last_of"); - m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); } ), "find_last_not_of"); - m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); } ), "find_first_not_of"); - - m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); } ), "+="); - - m.add(fun([](String *s) { s->clear(); } ), "clear"); - m.add(fun([](const String *s) { return s->empty(); } ), "empty"); - m.add(fun([](const String *s) { return s->size(); } ), "size"); - - m.add(fun([](const String *s) { return s->c_str(); } ), "c_str"); - m.add(fun([](const String *s) { return s->data(); } ), "data"); - m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); } ), "substr"); - } - template - ModulePtr string_type(const std::string &type) - { - auto m = std::make_shared(); - string_type(type, *m); - return m; - } - - - - /// Add a MapType container - /// http://www.sgi.com/tech/stl/Map.html - template - void future_type(const std::string &type, Module& m) - { - m.add(user_type(), type); - - m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); - m.add(fun([](FutureType &t) { return t.get(); }), "get"); - m.add(fun(&FutureType::wait), "wait"); - } - template - ModulePtr future_type(const std::string &type) - { - auto m = std::make_shared(); - future_type(type, *m); - return m; - } + } )"); } } -} + /// Add a String container + /// http://www.sgi.com/tech/stl/basic_string.html + template + void string_type(const std::string &type, Module &m) { + m.add(user_type(), type); + operators::addition(m); + operators::assign_sum(m); + opers_comparison(m); + random_access_container_type(type, m); + sequence_type(type, m); + default_constructible_type(type, m); + // container_type(type, m); + assignable_type(type, m); + input_range_type(type, m); + + // Special case: add push_back to string (which doesn't support other back_insertion operations + m.add(fun(&String::push_back), []() -> std::string { + if (typeid(typename String::value_type) == typeid(Boxed_Value)) { + return "push_back_ref"; + } else { + return "push_back"; + } + }()); + + m.add(fun([](const String *s, const String &f, size_t pos) { return s->find(f, pos); }), "find"); + m.add(fun([](const String *s, const String &f, size_t pos) { return s->rfind(f, pos); }), "rfind"); + m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_of(f, pos); }), "find_first_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_of(f, pos); }), "find_last_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_last_not_of(f, pos); }), "find_last_not_of"); + m.add(fun([](const String *s, const String &f, size_t pos) { return s->find_first_not_of(f, pos); }), "find_first_not_of"); + + m.add(fun([](String *s, typename String::value_type c) -> decltype(auto) { return (*s += c); }), "+="); + + m.add(fun([](String *s) { s->clear(); }), "clear"); + m.add(fun([](const String *s) { return s->empty(); }), "empty"); + m.add(fun([](const String *s) { return s->size(); }), "size"); + + m.add(fun([](const String *s) { return s->c_str(); }), "c_str"); + m.add(fun([](const String *s) { return s->data(); }), "data"); + m.add(fun([](const String *s, size_t pos, size_t len) { return s->substr(pos, len); }), "substr"); + } + + /// Add a MapType container + /// http://www.sgi.com/tech/stl/Map.html + template + void future_type(const std::string &type, Module &m) { + m.add(user_type(), type); + + m.add(fun([](const FutureType &t) { return t.valid(); }), "valid"); + m.add(fun([](FutureType &t) { return t.get(); }), "get"); + m.add(fun(&FutureType::wait), "wait"); + } +} // namespace chaiscript::bootstrap::standard_library #endif - - diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index b79d735b..d4dd5883 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BOXED_CAST_HPP_ #define CHAISCRIPT_BOXED_CAST_HPP_ @@ -19,23 +18,19 @@ #include "type_info.hpp" namespace chaiscript { -class Type_Conversions; -namespace detail { -namespace exception { -class bad_any_cast; -} // namespace exception -} // namespace detail -} // namespace chaiscript + class Type_Conversions; +} +namespace chaiscript::detail::exception { + class bad_any_cast; +} // namespace chaiscript::detail::exception -namespace chaiscript -{ - +namespace chaiscript { /// \brief Function for extracting a value stored in a Boxed_Value object /// \tparam Type The type to extract from the Boxed_Value /// \param[in] bv The Boxed_Value to extract a typed value from - /// \returns Type equivalent to the requested type + /// \returns Type equivalent to the requested type /// \throws exception::bad_boxed_cast If the requested conversion is not possible - /// + /// /// boxed_cast will attempt to make conversions between value, &, *, std::shared_ptr, std::reference_wrapper, /// and std::function (const and non-const) where possible. boxed_cast is used internally during function /// dispatch. This means that all of these conversions will be attempted automatically for you during @@ -67,47 +62,41 @@ namespace chaiscript /// std::function conversion example /// \code /// chaiscript::ChaiScript chai; - /// Boxed_Value bv = chai.eval("`+`"); // Get the functor for the + operator which is built in + /// Boxed_Value bv = chai.eval("`+`"); // Get the functor for the + operator which is built in /// std::function f = chaiscript::boxed_cast >(bv); /// int i = f(2,3); /// assert(i == 5); /// \endcode template - decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) - { + decltype(auto) boxed_cast(const Boxed_Value &bv, const Type_Conversions_State *t_conversions = nullptr) { if (!t_conversions || bv.get_type_info().bare_equal(user_type()) || (t_conversions && !(*t_conversions)->convertable_type())) { try { - return(detail::Cast_Helper::cast(bv, t_conversions)); + return detail::Cast_Helper::cast(bv, t_conversions); } catch (const chaiscript::detail::exception::bad_any_cast &) { } } - - if (t_conversions && (*t_conversions)->convertable_type()) - { + if (t_conversions && (*t_conversions)->convertable_type()) { try { // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it // either way, we are not responsible if it doesn't work - return(detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions)); + return (detail::Cast_Helper::cast((*t_conversions)->boxed_type_conversion(t_conversions->saves(), bv), t_conversions)); } catch (...) { try { // try going the other way - return(detail::Cast_Helper::cast((*t_conversions)->boxed_type_down_conversion(t_conversions->saves(), bv), t_conversions)); + return (detail::Cast_Helper::cast((*t_conversions)->boxed_type_down_conversion(t_conversions->saves(), bv), + t_conversions)); } catch (const chaiscript::detail::exception::bad_any_cast &) { throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); } } } else { - // If it's not convertable, just throw the error, don't waste the time on the + // If it's not convertable, just throw the error, don't waste the time on the // attempted dynamic_cast throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); } - } -} - - +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp index b00dd045..fc547b18 100644 --- a/include/chaiscript/dispatchkit/boxed_cast_helper.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast_helper.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BOXED_CAST_HELPER_HPP_ #define CHAISCRIPT_BOXED_CAST_HELPER_HPP_ @@ -17,21 +16,19 @@ #include "boxed_value.hpp" #include "type_info.hpp" - -namespace chaiscript -{ +namespace chaiscript { class Type_Conversions_State; - namespace detail - { + namespace detail { // Cast_Helper_Inner helper classes template - T* throw_if_null(T *t) - { - if (t) { return t; } - throw std::runtime_error("Attempted to dereference null Boxed_Value"); + constexpr T *throw_if_null(T *t) { + if (t) { + return t; } + throw std::runtime_error("Attempted to dereference null Boxed_Value"); + } template static const T *verify_type_no_throw(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) { @@ -51,7 +48,6 @@ namespace chaiscript } } - template static const T *verify_type(const Boxed_Value &ob, const std::type_info &ti, const T *ptr) { if (ob.get_type_info().bare_equal_type_info(ti)) { @@ -72,252 +68,193 @@ namespace chaiscript /// Generic Cast_Helper_Inner, for casting to any type template - struct Cast_Helper_Inner - { - static Result cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); - } - }; + struct Cast_Helper_Inner { + static Result cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); + } + }; template - struct Cast_Helper_Inner : Cast_Helper_Inner - { - }; - + struct Cast_Helper_Inner : Cast_Helper_Inner { + }; /// Cast_Helper_Inner for casting to a const * type template - struct Cast_Helper_Inner - { - static const Result * cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr())); - } - }; + struct Cast_Helper_Inner { + static const Result *cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_const_ptr())); + } + }; /// Cast_Helper_Inner for casting to a * type template - struct Cast_Helper_Inner - { - static Result * cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_ptr())); - } - }; - - template - struct Cast_Helper_Inner : public Cast_Helper_Inner - { + struct Cast_Helper_Inner { + static Result *cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return static_cast(verify_type_no_throw(ob, typeid(Result), ob.get_ptr())); + } }; template - struct Cast_Helper_Inner : public Cast_Helper_Inner - { + struct Cast_Helper_Inner : public Cast_Helper_Inner { }; + template + struct Cast_Helper_Inner : public Cast_Helper_Inner { + }; /// Cast_Helper_Inner for casting to a & type template - struct Cast_Helper_Inner - { - static const Result & cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); - } - }; - - + struct Cast_Helper_Inner { + static const Result &cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *static_cast(verify_type(ob, typeid(Result), ob.get_const_ptr())); + } + }; /// Cast_Helper_Inner for casting to a & type template - struct Cast_Helper_Inner - { - static Result& cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return *static_cast(verify_type(ob, typeid(Result), ob.get_ptr())); - } - }; + struct Cast_Helper_Inner { + static Result &cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *static_cast(verify_type(ob, typeid(Result), ob.get_ptr())); + } + }; /// Cast_Helper_Inner for casting to a && type template - struct Cast_Helper_Inner - { - static Result&& cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return std::move(*static_cast(verify_type(ob, typeid(Result), ob.get_ptr()))); - } - }; + struct Cast_Helper_Inner { + static Result &&cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return std::move(*static_cast(verify_type(ob, typeid(Result), ob.get_ptr()))); + } + }; /// Cast_Helper_Inner for casting to a std::unique_ptr<> && type /// \todo Fix the fact that this has to be in a shared_ptr for now template - struct Cast_Helper_Inner &&> - { - static std::unique_ptr &&cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return std::move(*(ob.get().cast>>())); - } - }; + struct Cast_Helper_Inner &&> { + static std::unique_ptr &&cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return std::move(*(ob.get().cast>>())); + } + }; /// Cast_Helper_Inner for casting to a std::unique_ptr<> & type /// \todo Fix the fact that this has to be in a shared_ptr for now template - struct Cast_Helper_Inner &> - { - static std::unique_ptr &cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return *(ob.get().cast>>()); - } - }; + struct Cast_Helper_Inner &> { + static std::unique_ptr &cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *(ob.get().cast>>()); + } + }; /// Cast_Helper_Inner for casting to a std::unique_ptr<> & type /// \todo Fix the fact that this has to be in a shared_ptr for now template - struct Cast_Helper_Inner &> - { - static std::unique_ptr &cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return *(ob.get().cast>>()); - } - }; - + struct Cast_Helper_Inner &> { + static std::unique_ptr &cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return *(ob.get().cast>>()); + } + }; /// Cast_Helper_Inner for casting to a std::shared_ptr<> type template - struct Cast_Helper_Inner > - { - static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return ob.get().cast >(); - } - }; + struct Cast_Helper_Inner> { + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob.get().cast>(); } + }; /// Cast_Helper_Inner for casting to a std::shared_ptr type template - struct Cast_Helper_Inner > - { - static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - if (!ob.get_type_info().is_const()) - { - return std::const_pointer_cast(ob.get().cast >()); - } else { - return ob.get().cast >(); - } + struct Cast_Helper_Inner> { + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { + if (!ob.get_type_info().is_const()) { + return std::const_pointer_cast(ob.get().cast>()); + } else { + return ob.get().cast>(); } - }; + } + }; /// Cast_Helper_Inner for casting to a const std::shared_ptr<> & type template - struct Cast_Helper_Inner > : Cast_Helper_Inner > - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner> { + }; template - struct Cast_Helper_Inner &> : Cast_Helper_Inner > - { - }; + struct Cast_Helper_Inner &> : Cast_Helper_Inner> { + }; template - struct Cast_Helper_Inner &> - { - static_assert(!std::is_const::value, "Non-const reference to std::shared_ptr is not supported"); - static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - std::shared_ptr &res = ob.get().cast >(); - return ob.pointer_sentinel(res); - } - }; - + struct Cast_Helper_Inner &> { + static_assert(!std::is_const::value, "Non-const reference to std::shared_ptr is not supported"); + static auto cast(const Boxed_Value &ob, const Type_Conversions_State *) { + std::shared_ptr &res = ob.get().cast>(); + return ob.pointer_sentinel(res); + } + }; /// Cast_Helper_Inner for casting to a const std::shared_ptr & type template - struct Cast_Helper_Inner > : Cast_Helper_Inner > - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner> { + }; template - struct Cast_Helper_Inner &> : Cast_Helper_Inner > - { - }; - + struct Cast_Helper_Inner &> : Cast_Helper_Inner> { + }; /// Cast_Helper_Inner for casting to a Boxed_Value type template<> - struct Cast_Helper_Inner - { - static Boxed_Value cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return ob; - } - }; + struct Cast_Helper_Inner { + static Boxed_Value cast(const Boxed_Value &ob, const Type_Conversions_State *) { return ob; } + }; /// Cast_Helper_Inner for casting to a Boxed_Value & type template<> - struct Cast_Helper_Inner - { - static std::reference_wrapper cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return std::ref(const_cast(ob)); - } - }; - + struct Cast_Helper_Inner { + static std::reference_wrapper cast(const Boxed_Value &ob, const Type_Conversions_State *) { + return std::ref(const_cast(ob)); + } + }; /// Cast_Helper_Inner for casting to a const Boxed_Value & type template<> - struct Cast_Helper_Inner : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner : Cast_Helper_Inner { + }; template<> - struct Cast_Helper_Inner : Cast_Helper_Inner - { - }; - + struct Cast_Helper_Inner : Cast_Helper_Inner { + }; /// Cast_Helper_Inner for casting to a std::reference_wrapper type template - struct Cast_Helper_Inner > : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner { + }; template - struct Cast_Helper_Inner > : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner { + }; template - struct Cast_Helper_Inner &> : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner &> : Cast_Helper_Inner { + }; template - struct Cast_Helper_Inner > : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner { + }; template - struct Cast_Helper_Inner > : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner> : Cast_Helper_Inner { + }; template - struct Cast_Helper_Inner & > : Cast_Helper_Inner - { - }; + struct Cast_Helper_Inner &> : Cast_Helper_Inner { + }; /// The exposed Cast_Helper object that by default just calls the Cast_Helper_Inner template - struct Cast_Helper - { - static decltype(auto) cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) - { - return(Cast_Helper_Inner::cast(ob, t_conversions)); - } - }; - } - -} + struct Cast_Helper { + static decltype(auto) cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + return (Cast_Helper_Inner::cast(ob, t_conversions)); + } + }; + } // namespace detail + +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index dafc1234..816fe5a3 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. @@ -22,25 +22,20 @@ #include "type_info.hpp" namespace chaiscript { -class Type_Conversions; -} // namespace chaiscript + class Type_Conversions; +} // namespace chaiscript -namespace chaiscript -{ - namespace exception - { - struct arithmetic_error : std::runtime_error - { - explicit arithmetic_error(const std::string& reason) : std::runtime_error("Arithmetic error: " + reason) {} - arithmetic_error(const arithmetic_error &) = default; - ~arithmetic_error() noexcept override = default; - }; - } -} - -namespace chaiscript -{ +namespace chaiscript::exception { + struct arithmetic_error : std::runtime_error { + explicit arithmetic_error(const std::string &reason) + : std::runtime_error("Arithmetic error: " + reason) { + } + arithmetic_error(const arithmetic_error &) = default; + ~arithmetic_error() noexcept override = default; + }; +} // namespace chaiscript::exception +namespace chaiscript { // Due to the nature of generating every possible arithmetic operation, there // are going to be warnings generated on every platform regarding size and sign, // this is OK, so we're disabling size/and sign type warnings @@ -49,7 +44,6 @@ namespace chaiscript #pragma warning(disable : 4244 4018 4389 4146 4365 4267 4242) #endif - #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" @@ -59,876 +53,652 @@ namespace chaiscript #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wsign-conversion" #pragma GCC diagnostic ignored "-Wfloat-conversion" +#pragma GCC diagnostic ignored "-Wswitch" #endif /// \brief Represents any numeric type, generically. Used internally for generic operations between POD values - class Boxed_Number - { - private: - enum class Common_Types { - t_int32, - t_double, - t_uint8, - t_int8, - t_uint16, - t_int16, - t_uint32, - t_uint64, - t_int64, - t_float, - t_long_double - }; + class Boxed_Number { + private: + enum class Common_Types { + t_int32, + t_double, + t_uint8, + t_int8, + t_uint16, + t_int16, + t_uint32, + t_uint64, + t_int64, + t_float, + t_long_double + }; - template - static inline void check_divide_by_zero(T t, typename std::enable_if::value>::type* = nullptr) - { + template + constexpr static inline void check_divide_by_zero([[maybe_unused]] T t) { #ifndef CHAISCRIPT_NO_PROTECT_DIVIDEBYZERO + if constexpr (!std::is_floating_point::value) { if (t == 0) { throw chaiscript::exception::arithmetic_error("divide by zero"); } + } #endif + } + + constexpr static Common_Types get_common_type(size_t t_size, bool t_signed) noexcept { + return (t_size == 1 && t_signed) ? (Common_Types::t_int8) + : (t_size == 1) ? (Common_Types::t_uint8) + : (t_size == 2 && t_signed) ? (Common_Types::t_int16) + : (t_size == 2) ? (Common_Types::t_uint16) + : (t_size == 4 && t_signed) ? (Common_Types::t_int32) + : (t_size == 4) ? (Common_Types::t_uint32) + : (t_size == 8 && t_signed) ? (Common_Types::t_int64) + : (Common_Types::t_uint64); + } + + static Common_Types get_common_type(const Boxed_Value &t_bv) { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == user_type()) { + return get_common_type(sizeof(int), true); + } else if (inp_ == user_type()) { + return Common_Types::t_double; + } else if (inp_ == user_type()) { + return Common_Types::t_long_double; + } else if (inp_ == user_type()) { + return Common_Types::t_float; + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char), std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned char), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned int), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(long), true); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(long long), true); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned long), false); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(unsigned long long), false); + } else if (inp_ == user_type()) { + return Common_Types::t_int8; + } else if (inp_ == user_type()) { + return Common_Types::t_int16; + } else if (inp_ == user_type()) { + return Common_Types::t_int32; + } else if (inp_ == user_type()) { + return Common_Types::t_int64; + } else if (inp_ == user_type()) { + return Common_Types::t_uint8; + } else if (inp_ == user_type()) { + return Common_Types::t_uint16; + } else if (inp_ == user_type()) { + return Common_Types::t_uint32; + } else if (inp_ == user_type()) { + return Common_Types::t_uint64; + } else if (inp_ == user_type()) { + return get_common_type(sizeof(wchar_t), std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char16_t), std::is_signed::value); + } else if (inp_ == user_type()) { + return get_common_type(sizeof(char32_t), std::is_signed::value); + } else { + throw chaiscript::detail::exception::bad_any_cast(); + } + } + + template + static auto go(Operators::Opers t_oper, const Boxed_Value &t_bv, LHS *t_lhs, const LHS &c_lhs, const RHS &c_rhs) { + switch (t_oper) { + case Operators::Opers::equals: + return const_var(c_lhs == c_rhs); + case Operators::Opers::less_than: + return const_var(c_lhs < c_rhs); + case Operators::Opers::greater_than: + return const_var(c_lhs > c_rhs); + case Operators::Opers::less_than_equal: + return const_var(c_lhs <= c_rhs); + case Operators::Opers::greater_than_equal: + return const_var(c_lhs >= c_rhs); + case Operators::Opers::not_equal: + return const_var(c_lhs != c_rhs); + case Operators::Opers::sum: + return const_var(c_lhs + c_rhs); + case Operators::Opers::quotient: + check_divide_by_zero(c_rhs); + return const_var(c_lhs / c_rhs); + case Operators::Opers::product: + return const_var(c_lhs * c_rhs); + case Operators::Opers::difference: + return const_var(c_lhs - c_rhs); + default: + break; } - template - static inline void check_divide_by_zero(T, typename std::enable_if::value>::type* = nullptr) - { - } - - static constexpr Common_Types get_common_type(size_t t_size, bool t_signed) - { - return (t_size == 1 && t_signed)?(Common_Types::t_int8) - :(t_size == 1)?(Common_Types::t_uint8) - :(t_size == 2 && t_signed)?(Common_Types::t_int16) - :(t_size == 2)?(Common_Types::t_uint16) - :(t_size == 4 && t_signed)?(Common_Types::t_int32) - :(t_size == 4)?(Common_Types::t_uint32) - :(t_size == 8 && t_signed)?(Common_Types::t_int64) - :(Common_Types::t_uint64); - } - - - static Common_Types get_common_type(const Boxed_Value &t_bv) - { - const Type_Info &inp_ = t_bv.get_type_info(); - - if (inp_ == typeid(int)) { - return get_common_type(sizeof(int), true); - } else if (inp_ == typeid(double)) { - return Common_Types::t_double; - } else if (inp_ == typeid(long double)) { - return Common_Types::t_long_double; - } else if (inp_ == typeid(float)) { - return Common_Types::t_float; - } else if (inp_ == typeid(char)) { - return get_common_type(sizeof(char), std::is_signed::value); - } else if (inp_ == typeid(unsigned char)) { - return get_common_type(sizeof(unsigned char), false); - } else if (inp_ == typeid(unsigned int)) { - return get_common_type(sizeof(unsigned int), false); - } else if (inp_ == typeid(long)) { - return get_common_type(sizeof(long), true); - } else if (inp_ == typeid(long long)) { - return get_common_type(sizeof(long long), true); - } else if (inp_ == typeid(unsigned long)) { - return get_common_type(sizeof(unsigned long), false); - } else if (inp_ == typeid(unsigned long long)) { - return get_common_type(sizeof(unsigned long long), false); - } else if (inp_ == typeid(std::int8_t)) { - return Common_Types::t_int8; - } else if (inp_ == typeid(std::int16_t)) { - return Common_Types::t_int16; - } else if (inp_ == typeid(std::int32_t)) { - return Common_Types::t_int32; - } else if (inp_ == typeid(std::int64_t)) { - return Common_Types::t_int64; - } else if (inp_ == typeid(std::uint8_t)) { - return Common_Types::t_uint8; - } else if (inp_ == typeid(std::uint16_t)) { - return Common_Types::t_uint16; - } else if (inp_ == typeid(std::uint32_t)) { - return Common_Types::t_uint32; - } else if (inp_ == typeid(std::uint64_t)) { - return Common_Types::t_uint64; - } else if (inp_ == typeid(wchar_t)) { - return get_common_type(sizeof(wchar_t), std::is_signed::value); - } else if (inp_ == typeid(char16_t)) { - return get_common_type(sizeof(char16_t), std::is_signed::value); - } else if (inp_ == typeid(char32_t)) { - return get_common_type(sizeof(char32_t), std::is_signed::value); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static Boxed_Value boolean_go(Operators::Opers t_oper, const T &t, const T &u) - { - switch (t_oper) - { - case Operators::Opers::equals: - return const_var(t == u); - case Operators::Opers::less_than: - return const_var(t < u); - case Operators::Opers::greater_than: - return const_var(t > u); - case Operators::Opers::less_than_equal: - return const_var(t <= u); - case Operators::Opers::greater_than_equal: - return const_var(t >= u); - case Operators::Opers::not_equal: - return const_var(t != u); - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static Boxed_Value unary_go(Operators::Opers t_oper, T &t, const Boxed_Value &t_lhs) - { - switch (t_oper) - { - case Operators::Opers::pre_increment: - ++t; - break; - case Operators::Opers::pre_decrement: - --t; - break; - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - - return t_lhs; - } - - template - static Boxed_Value binary_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) - { - switch (t_oper) - { - case Operators::Opers::assign: - t = u; - break; - case Operators::Opers::assign_product: - t *= u; - break; - case Operators::Opers::assign_sum: - t += u; - break; - case Operators::Opers::assign_quotient: - check_divide_by_zero(u); - t /= u; - break; - case Operators::Opers::assign_difference: - t -= u; - break; - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - - return t_lhs; - } - - template - static Boxed_Value binary_int_go(Operators::Opers t_oper, T &t, const U &u, const Boxed_Value &t_lhs) - { - switch (t_oper) - { - case Operators::Opers::assign_bitwise_and: - t &= u; - break; - case Operators::Opers::assign_bitwise_or: - t |= u; - break; - case Operators::Opers::assign_shift_left: - t <<= u; - break; - case Operators::Opers::assign_shift_right: - t >>= u; - break; - case Operators::Opers::assign_remainder: - check_divide_by_zero(u); - t %= u; - break; - case Operators::Opers::assign_bitwise_xor: - t ^= u; - break; - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - return t_lhs; - } - - template - static Boxed_Value const_unary_int_go(Operators::Opers t_oper, const T &t) - { - switch (t_oper) - { - case Operators::Opers::bitwise_complement: - return const_var(~t); - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static Boxed_Value const_binary_int_go(Operators::Opers t_oper, const T &t, const T &u) - { - switch (t_oper) - { + if constexpr (!std::is_floating_point::value && !std::is_floating_point::value) { + switch (t_oper) { case Operators::Opers::shift_left: - return const_var(t << u); + return const_var(c_lhs << c_rhs); case Operators::Opers::shift_right: - return const_var(t >> u); + return const_var(c_lhs >> c_rhs); case Operators::Opers::remainder: - check_divide_by_zero(u); - return const_var(t % u); + check_divide_by_zero(c_rhs); + return const_var(c_lhs % c_rhs); case Operators::Opers::bitwise_and: - return const_var(t & u); + return const_var(c_lhs & c_rhs); case Operators::Opers::bitwise_or: - return const_var(t | u); + return const_var(c_lhs | c_rhs); case Operators::Opers::bitwise_xor: - return const_var(t ^ u); + return const_var(c_lhs ^ c_rhs); default: - throw chaiscript::detail::exception::bad_any_cast(); + break; } } - template - static Boxed_Value const_unary_go(Operators::Opers t_oper, const T &t) - { - switch (t_oper) - { + if (t_lhs) { + switch (t_oper) { + case Operators::Opers::assign: + *t_lhs = c_rhs; + return t_bv; + case Operators::Opers::assign_product: + *t_lhs *= c_rhs; + return t_bv; + case Operators::Opers::assign_sum: + *t_lhs += c_rhs; + return t_bv; + case Operators::Opers::assign_quotient: + check_divide_by_zero(c_rhs); + *t_lhs /= c_rhs; + return t_bv; + case Operators::Opers::assign_difference: + *t_lhs -= c_rhs; + return t_bv; + default: + break; + } + + if constexpr (!std::is_floating_point::value && !std::is_floating_point::value) { + switch (t_oper) { + case Operators::Opers::assign_bitwise_and: + check_divide_by_zero(c_rhs); + *t_lhs &= c_rhs; + return t_bv; + case Operators::Opers::assign_bitwise_or: + *t_lhs |= c_rhs; + return t_bv; + case Operators::Opers::assign_shift_left: + *t_lhs <<= c_rhs; + return t_bv; + case Operators::Opers::assign_shift_right: + *t_lhs >>= c_rhs; + return t_bv; + case Operators::Opers::assign_remainder: + *t_lhs %= c_rhs; + return t_bv; + case Operators::Opers::assign_bitwise_xor: + *t_lhs ^= c_rhs; + return t_bv; + default: + break; + } + } + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + template + inline static auto visit(const Boxed_Value &bv, Callable &&callable) { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_uint8: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_int8: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_uint16: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_int16: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_uint32: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_uint64: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_int64: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_double: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_float: + return callable(*static_cast(bv.get_const_ptr())); + case Common_Types::t_long_double: + return callable(*static_cast(bv.get_const_ptr())); + } + throw chaiscript::detail::exception::bad_any_cast(); + } + + inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) { + auto unary_operator = [t_oper, &t_lhs](const auto &c_lhs) { + auto *lhs = static_cast *>(t_lhs.get_ptr()); + + if (lhs) { + switch (t_oper) { + case Operators::Opers::pre_increment: + ++(*lhs); + return t_lhs; + case Operators::Opers::pre_decrement: + --(*lhs); + return t_lhs; + default: + break; + } + } + + switch (t_oper) { case Operators::Opers::unary_minus: - return const_var(-t); + return const_var(-c_lhs); case Operators::Opers::unary_plus: - return const_var(+t); + return const_var(+c_lhs); default: - throw chaiscript::detail::exception::bad_any_cast(); + break; } - } - template - static Boxed_Value const_binary_go(Operators::Opers t_oper, const T &t, const T &u) - { - switch (t_oper) - { - case Operators::Opers::sum: - return const_var(t + u); - case Operators::Opers::quotient: - check_divide_by_zero(u); - return const_var(t / u); - case Operators::Opers::product: - return const_var(t * u); - case Operators::Opers::difference: - return const_var(t - u); - default: - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) - -> typename std::enable_if::value && !std::is_floating_point::value, Boxed_Value>::type - { - typedef typename std::common_type::type common_type; - if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag) - { - return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { - return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::Opers::non_const_int_flag && t_oper < Operators::Opers::const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { - return binary_int_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) { - return const_binary_int_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::Opers::const_flag) { - return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) - -> typename std::enable_if::value || std::is_floating_point::value, Boxed_Value>::type - { - typedef typename std::common_type::type common_type; - if (t_oper > Operators::Opers::boolean_flag && t_oper < Operators::Opers::non_const_flag) - { - return boolean_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { - return binary_go(t_oper, *static_cast(t_lhs.get_ptr()), get_as_aux(t_rhs), t_lhs); - } else if (t_oper > Operators::Opers::const_flag) { - return const_binary_go(t_oper, get_as_aux(t_lhs), get_as_aux(t_rhs)); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - // Unary - template - static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) - -> typename std::enable_if::value, Boxed_Value>::type - { - if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { - return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); - } else if (t_oper > Operators::Opers::const_int_flag && t_oper < Operators::Opers::const_flag) { - return const_unary_int_go(t_oper, *static_cast(t_lhs.get_const_ptr())); - } else if (t_oper > Operators::Opers::const_flag) { - return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - static auto go(Operators::Opers t_oper, const Boxed_Value &t_lhs) - -> typename std::enable_if::value, Boxed_Value>::type - { - if (t_oper > Operators::Opers::non_const_flag && t_oper < Operators::Opers::non_const_int_flag && !t_lhs.is_const() && !t_lhs.is_return_value()) { - return unary_go(t_oper, *static_cast(t_lhs.get_ptr()), t_lhs); - } else if (t_oper > Operators::Opers::const_flag) { - return const_unary_go(t_oper, *static_cast(t_lhs.get_const_ptr())); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - } - - template - inline static Boxed_Value oper_rhs(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) - { - switch (get_common_type(t_rhs)) { - case Common_Types::t_int32: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint8: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_int8: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint16: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_int16: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint32: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint64: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_int64: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_double: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_float: - return go(t_oper, t_lhs, t_rhs); - case Common_Types::t_long_double: - return go(t_oper, t_lhs, t_rhs); + if constexpr (!std::is_floating_point_v>) { + switch (t_oper) { + case Operators::Opers::bitwise_complement: + return const_var(~c_lhs); + default: + break; } - - throw chaiscript::detail::exception::bad_any_cast(); } - inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) - { - switch (get_common_type(t_lhs)) { - case Common_Types::t_int32: - return go(t_oper, t_lhs); - case Common_Types::t_uint8: - return go(t_oper, t_lhs); - case Common_Types::t_int8: - return go(t_oper, t_lhs); - case Common_Types::t_uint16: - return go(t_oper, t_lhs); - case Common_Types::t_int16: - return go(t_oper, t_lhs); - case Common_Types::t_uint32: - return go(t_oper, t_lhs); - case Common_Types::t_uint64: - return go(t_oper, t_lhs); - case Common_Types::t_int64: - return go(t_oper, t_lhs); - case Common_Types::t_double: - return go(t_oper, t_lhs); - case Common_Types::t_float: - return go(t_oper, t_lhs); - case Common_Types::t_long_double: - return go(t_oper, t_lhs); - } + throw chaiscript::detail::exception::bad_any_cast(); + }; - throw chaiscript::detail::exception::bad_any_cast(); - } + return visit(t_lhs, unary_operator); + } + inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) { + auto lhs_visit = [t_oper, &t_lhs, &t_rhs](const auto &c_lhs) { + auto *lhs = t_lhs.is_return_value() ? nullptr : static_cast *>(t_lhs.get_ptr()); - inline static Boxed_Value oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) - { - switch (get_common_type(t_lhs)) { - case Common_Types::t_int32: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint8: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_int8: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint16: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_int16: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint32: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_uint64: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_int64: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_double: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_float: - return oper_rhs(t_oper, t_lhs, t_rhs); - case Common_Types::t_long_double: - return oper_rhs(t_oper, t_lhs, t_rhs); - } + auto rhs_visit = [t_oper, &t_lhs, lhs, &c_lhs](const auto &c_rhs) { return go(t_oper, t_lhs, lhs, c_lhs, c_rhs); }; - throw chaiscript::detail::exception::bad_any_cast(); - } + return visit(t_rhs, rhs_visit); + }; - template - static inline Target get_as_aux(const Boxed_Value &t_bv) - { - return static_cast(*static_cast(t_bv.get_const_ptr())); - } + return visit(t_lhs, lhs_visit); + } - template - static std::string to_string_aux(const Boxed_Value &v) - { - std::ostringstream oss; - oss << *static_cast(v.get_const_ptr()); - return oss.str(); - } + template + static inline Target get_as_aux(const Boxed_Value &t_bv) { + return static_cast(*static_cast(t_bv.get_const_ptr())); + } - public: - Boxed_Number() - : bv(Boxed_Value(0)) - { + template + static std::string to_string_aux(const Boxed_Value &v) { + std::ostringstream oss; + oss << *static_cast(v.get_const_ptr()); + return oss.str(); + } + + public: + Boxed_Number() + : bv(Boxed_Value(0)) { + } + + explicit Boxed_Number(Boxed_Value v) + : bv(std::move(v)) { + validate_boxed_number(bv); + } + + Boxed_Number(const Boxed_Number &) = default; + Boxed_Number(Boxed_Number &&) = default; + Boxed_Number &operator=(Boxed_Number &&) = default; + + template + explicit Boxed_Number(T t) + : bv(Boxed_Value(t)) { + validate_boxed_number(bv); + } + + static Boxed_Value clone(const Boxed_Value &t_bv) { return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv; } + + static bool is_floating_point(const Boxed_Value &t_bv) { + const Type_Info &inp_ = t_bv.get_type_info(); + + if (inp_ == user_type()) { + return true; + } else if (inp_ == user_type()) { + return true; + } else if (inp_ == user_type()) { + return true; + } else { + return false; } + } - explicit Boxed_Number(Boxed_Value v) - : bv(std::move(v)) - { - validate_boxed_number(bv); + Boxed_Number get_as(const Type_Info &inp_) const { + if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else if (inp_.bare_equal(user_type())) { + return Boxed_Number(get_as()); + } else { + throw chaiscript::detail::exception::bad_any_cast(); } + } - Boxed_Number(const Boxed_Number &) = default; - Boxed_Number(Boxed_Number &&) = default; - Boxed_Number& operator=(Boxed_Number &&) = default; - - template explicit Boxed_Number(T t) - : bv(Boxed_Value(t)) - { - validate_boxed_number(bv); - } - - static Boxed_Value clone(const Boxed_Value &t_bv) { - return Boxed_Number(t_bv).get_as(t_bv.get_type_info()).bv; - } - - static bool is_floating_point(const Boxed_Value &t_bv) - { - const Type_Info &inp_ = t_bv.get_type_info(); - - if (inp_ == typeid(double)) { - return true; - } else if (inp_ == typeid(long double)) { - return true; - } else if (inp_ == typeid(float)) { - return true; - } else { - return false; - } - } - - Boxed_Number get_as(const Type_Info &inp_) const - { - if (inp_.bare_equal_type_info(typeid(int))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(double))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(float))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(long double))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(char))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(unsigned char))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(wchar_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(char16_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(char32_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(unsigned int))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(long))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(long long))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(unsigned long))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(unsigned long long))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(int8_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(int16_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(int32_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(int64_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(uint8_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(uint16_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(uint32_t))) { - return Boxed_Number(get_as()); - } else if (inp_.bare_equal_type_info(typeid(uint64_t))) { - return Boxed_Number(get_as()); - } else { - throw chaiscript::detail::exception::bad_any_cast(); - } - - } - - template - static void check_type() - { + template + static void check_type() { #ifdef CHAISCRIPT_MSVC // MSVC complains about this being redundant / tautologica l #pragma warning(push) #pragma warning(disable : 4127 6287) #endif - if (sizeof(Source) != sizeof(Target) - || std::is_signed() != std::is_signed() - || std::is_floating_point() != std::is_floating_point()) - { - throw chaiscript::detail::exception::bad_any_cast(); - } + if (sizeof(Source) != sizeof(Target) || std::is_signed() != std::is_signed() + || std::is_floating_point() != std::is_floating_point()) { + throw chaiscript::detail::exception::bad_any_cast(); + } #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif + } + + template + Target get_as_checked() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint8: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int8: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint16: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int16: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint32: + check_type(); + return get_as_aux(bv); + case Common_Types::t_uint64: + check_type(); + return get_as_aux(bv); + case Common_Types::t_int64: + check_type(); + return get_as_aux(bv); + case Common_Types::t_double: + check_type(); + return get_as_aux(bv); + case Common_Types::t_float: + check_type(); + return get_as_aux(bv); + case Common_Types::t_long_double: + check_type(); + return get_as_aux(bv); } - template Target get_as_checked() const - { - switch (get_common_type(bv)) { - case Common_Types::t_int32: - check_type(); - return get_as_aux(bv); - case Common_Types::t_uint8: - check_type(); - return get_as_aux(bv); - case Common_Types::t_int8: - check_type(); - return get_as_aux(bv); - case Common_Types::t_uint16: - check_type(); - return get_as_aux(bv); - case Common_Types::t_int16: - check_type(); - return get_as_aux(bv); - case Common_Types::t_uint32: - check_type(); - return get_as_aux(bv); - case Common_Types::t_uint64: - check_type(); - return get_as_aux(bv); - case Common_Types::t_int64: - check_type(); - return get_as_aux(bv); - case Common_Types::t_double: - check_type(); - return get_as_aux(bv); - case Common_Types::t_float: - check_type(); - return get_as_aux(bv); - case Common_Types::t_long_double: - check_type(); - return get_as_aux(bv); - } + throw chaiscript::detail::exception::bad_any_cast(); + } + template + Target get_as() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return get_as_aux(bv); + case Common_Types::t_uint8: + return get_as_aux(bv); + case Common_Types::t_int8: + return get_as_aux(bv); + case Common_Types::t_uint16: + return get_as_aux(bv); + case Common_Types::t_int16: + return get_as_aux(bv); + case Common_Types::t_uint32: + return get_as_aux(bv); + case Common_Types::t_uint64: + return get_as_aux(bv); + case Common_Types::t_int64: + return get_as_aux(bv); + case Common_Types::t_double: + return get_as_aux(bv); + case Common_Types::t_float: + return get_as_aux(bv); + case Common_Types::t_long_double: + return get_as_aux(bv); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + std::string to_string() const { + switch (get_common_type(bv)) { + case Common_Types::t_int32: + return std::to_string(get_as()); + case Common_Types::t_uint8: + return std::to_string(get_as()); + case Common_Types::t_int8: + return std::to_string(get_as()); + case Common_Types::t_uint16: + return std::to_string(get_as()); + case Common_Types::t_int16: + return std::to_string(get_as()); + case Common_Types::t_uint32: + return std::to_string(get_as()); + case Common_Types::t_uint64: + return std::to_string(get_as()); + case Common_Types::t_int64: + return std::to_string(get_as()); + case Common_Types::t_double: + return to_string_aux(bv); + case Common_Types::t_float: + return to_string_aux(bv); + case Common_Types::t_long_double: + return to_string_aux(bv); + } + + throw chaiscript::detail::exception::bad_any_cast(); + } + + static void validate_boxed_number(const Boxed_Value &v) { + const Type_Info &inp_ = v.get_type_info(); + if (inp_ == user_type()) { throw chaiscript::detail::exception::bad_any_cast(); } - - template Target get_as() const - { - switch (get_common_type(bv)) { - case Common_Types::t_int32: - return get_as_aux(bv); - case Common_Types::t_uint8: - return get_as_aux(bv); - case Common_Types::t_int8: - return get_as_aux(bv); - case Common_Types::t_uint16: - return get_as_aux(bv); - case Common_Types::t_int16: - return get_as_aux(bv); - case Common_Types::t_uint32: - return get_as_aux(bv); - case Common_Types::t_uint64: - return get_as_aux(bv); - case Common_Types::t_int64: - return get_as_aux(bv); - case Common_Types::t_double: - return get_as_aux(bv); - case Common_Types::t_float: - return get_as_aux(bv); - case Common_Types::t_long_double: - return get_as_aux(bv); - } - + if (!inp_.is_arithmetic()) { throw chaiscript::detail::exception::bad_any_cast(); } + } - std::string to_string() const - { - switch (get_common_type(bv)) { - case Common_Types::t_int32: - return std::to_string(get_as()); - case Common_Types::t_uint8: - return std::to_string(get_as()); - case Common_Types::t_int8: - return std::to_string(get_as()); - case Common_Types::t_uint16: - return std::to_string(get_as()); - case Common_Types::t_int16: - return std::to_string(get_as()); - case Common_Types::t_uint32: - return std::to_string(get_as()); - case Common_Types::t_uint64: - return std::to_string(get_as()); - case Common_Types::t_int64: - return std::to_string(get_as()); - case Common_Types::t_double: - return to_string_aux(bv); - case Common_Types::t_float: - return to_string_aux(bv); - case Common_Types::t_long_double: - return to_string_aux(bv); - } + static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::equals, t_lhs.bv, t_rhs.bv)); + } - throw chaiscript::detail::exception::bad_any_cast(); - } + static bool less_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::less_than, t_lhs.bv, t_rhs.bv)); + } - static void validate_boxed_number(const Boxed_Value &v) - { - const Type_Info &inp_ = v.get_type_info(); - if (inp_ == typeid(bool)) - { - throw chaiscript::detail::exception::bad_any_cast(); - } + static bool greater_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::greater_than, t_lhs.bv, t_rhs.bv)); + } - if (!inp_.is_arithmetic()) - { - throw chaiscript::detail::exception::bad_any_cast(); - } - } + static bool greater_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::greater_than_equal, t_lhs.bv, t_rhs.bv)); + } + static bool less_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::less_than_equal, t_lhs.bv, t_rhs.bv)); + } + static bool not_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return boxed_cast(oper(Operators::Opers::not_equal, t_lhs.bv, t_rhs.bv)); + } - static bool equals(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::equals, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number pre_decrement(Boxed_Number t_lhs) { return Boxed_Number(oper(Operators::Opers::pre_decrement, t_lhs.bv)); } - static bool less_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::less_than, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number pre_increment(Boxed_Number t_lhs) { return Boxed_Number(oper(Operators::Opers::pre_increment, t_lhs.bv)); } - static bool greater_than(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::greater_than, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number sum(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::sum, t_lhs.bv, t_rhs.bv)); + } - static bool greater_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::greater_than_equal, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) { return Boxed_Number(oper(Operators::Opers::unary_plus, t_lhs.bv)); } - static bool less_than_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::less_than_equal, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number unary_minus(const Boxed_Number &t_lhs) { return Boxed_Number(oper(Operators::Opers::unary_minus, t_lhs.bv)); } - static bool not_equal(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return boxed_cast(oper(Operators::Opers::not_equal, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number difference(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::difference, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number pre_decrement(Boxed_Number t_lhs) - { - return Boxed_Number(oper(Operators::Opers::pre_decrement, t_lhs.bv)); - } + static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_bitwise_and, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number pre_increment(Boxed_Number t_lhs) - { - return Boxed_Number(oper(Operators::Opers::pre_increment, t_lhs.bv)); - } + static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number sum(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::sum, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_bitwise_or, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number unary_plus(const Boxed_Number &t_lhs) - { - return Boxed_Number(oper(Operators::Opers::unary_plus, t_lhs.bv)); - } + static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_bitwise_xor, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number unary_minus(const Boxed_Number &t_lhs) - { - return Boxed_Number(oper(Operators::Opers::unary_minus, t_lhs.bv)); - } + static Boxed_Number assign_remainder(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_remainder, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number difference(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::difference, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_shift_left(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_shift_left, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_bitwise_and(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_bitwise_and, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_shift_right(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_shift_right, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::bitwise_and, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_bitwise_or(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_bitwise_or, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs) { + return Boxed_Number(oper(Operators::Opers::bitwise_complement, t_lhs.bv, Boxed_Value(0))); + } - static Boxed_Number assign_bitwise_xor(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_bitwise_xor, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::bitwise_xor, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_remainder(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_remainder, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::bitwise_or, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_shift_left(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_shift_left, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_product(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_product, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_shift_right(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_shift_right, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_quotient(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_quotient, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number bitwise_and(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::bitwise_and, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Number assign_sum(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_sum, t_lhs.bv, t_rhs.bv)); + } + static Boxed_Number assign_difference(Boxed_Number t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::assign_difference, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number bitwise_complement(const Boxed_Number &t_lhs) - { - return Boxed_Number(oper(Operators::Opers::bitwise_complement, t_lhs.bv, Boxed_Value(0))); - } + static const Boxed_Number quotient(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::quotient, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number bitwise_xor(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::bitwise_xor, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number shift_left(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::shift_left, t_lhs.bv, t_rhs.bv)); + } - static const Boxed_Number bitwise_or(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::bitwise_or, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number product(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::product, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_product(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_product, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number remainder(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::remainder, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_quotient(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_quotient, t_lhs.bv, t_rhs.bv)); - } + static const Boxed_Number shift_right(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) { + return Boxed_Number(oper(Operators::Opers::shift_right, t_lhs.bv, t_rhs.bv)); + } - static Boxed_Number assign_sum(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_sum, t_lhs.bv, t_rhs.bv)); - } - static Boxed_Number assign_difference(Boxed_Number t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::assign_difference, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) { + return oper(t_oper, t_lhs, t_rhs); + } - static const Boxed_Number quotient(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::quotient, t_lhs.bv, t_rhs.bv)); - } + static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) { return oper(t_oper, t_lhs); } - static const Boxed_Number shift_left(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::shift_left, t_lhs.bv, t_rhs.bv)); - } - - static const Boxed_Number product(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::product, t_lhs.bv, t_rhs.bv)); - } - - static const Boxed_Number remainder(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::remainder, t_lhs.bv, t_rhs.bv)); - } - - static const Boxed_Number shift_right(const Boxed_Number &t_lhs, const Boxed_Number &t_rhs) - { - return Boxed_Number(oper(Operators::Opers::shift_right, t_lhs.bv, t_rhs.bv)); - } - - - - static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) - { - return oper(t_oper, t_lhs, t_rhs); - } - - static Boxed_Value do_oper(Operators::Opers t_oper, const Boxed_Value &t_lhs) - { - return oper(t_oper, t_lhs); - } - - - - Boxed_Value bv; + Boxed_Value bv; }; - namespace detail - { + namespace detail { /// Cast_Helper for converting from Boxed_Value to Boxed_Number template<> - struct Cast_Helper - { - static Boxed_Number cast(const Boxed_Value &ob, const Type_Conversions_State *) - { - return Boxed_Number(ob); - } - }; + struct Cast_Helper { + static Boxed_Number cast(const Boxed_Value &ob, const Type_Conversions_State *) { return Boxed_Number(ob); } + }; /// Cast_Helper for converting from Boxed_Value to Boxed_Number template<> - struct Cast_Helper : Cast_Helper - { - }; + struct Cast_Helper : Cast_Helper { + }; /// Cast_Helper for converting from Boxed_Value to Boxed_Number template<> - struct Cast_Helper : Cast_Helper - { - }; - } + struct Cast_Helper : Cast_Helper { + }; + } // namespace detail #ifdef __GNUC__ #pragma GCC diagnostic pop @@ -938,9 +708,6 @@ namespace chaiscript #pragma warning(pop) #endif -} - - +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index ce943eb9..06941b2f 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_BOXED_VALUE_HPP_ #define CHAISCRIPT_BOXED_VALUE_HPP_ @@ -19,357 +18,238 @@ #include "any.hpp" #include "type_info.hpp" -namespace chaiscript -{ - +namespace chaiscript { /// \brief A wrapper for holding any valid C++ type. All types in ChaiScript are Boxed_Value objects /// \sa chaiscript::boxed_cast - class Boxed_Value - { - public: - /// used for explicitly creating a "void" object - struct Void_Type - { - }; + class Boxed_Value { + public: + /// used for explicitly creating a "void" object + struct Void_Type { + }; - private: - /// structure which holds the internal state of a Boxed_Value - /// \todo Get rid of Any and merge it with this, reducing an allocation in the process - struct Data - { - Data(const Type_Info &ti, - chaiscript::detail::Any to, - bool is_ref, - const void *t_void_ptr, - bool t_return_value) - : m_type_info(ti), m_obj(std::move(to)), m_data_ptr(ti.is_const()?nullptr:const_cast(t_void_ptr)), m_const_data_ptr(t_void_ptr), - m_is_ref(is_ref), m_return_value(t_return_value) - { - } - - Data &operator=(const Data &rhs) - { - m_type_info = rhs.m_type_info; - m_obj = rhs.m_obj; - m_is_ref = rhs.m_is_ref; - m_data_ptr = rhs.m_data_ptr; - m_const_data_ptr = rhs.m_const_data_ptr; - m_return_value = rhs.m_return_value; - - if (rhs.m_attrs) - { - m_attrs = std::make_unique>>(*rhs.m_attrs); - } - - return *this; - } - - Data(const Data &) = delete; - - Data(Data &&) = default; - Data &operator=(Data &&rhs) = default; - - - Type_Info m_type_info; - chaiscript::detail::Any m_obj; - void *m_data_ptr; - const void *m_const_data_ptr; - std::unique_ptr>> m_attrs; - bool m_is_ref; - bool m_return_value; - }; - - struct Object_Data - { - static auto get(Boxed_Value::Void_Type, bool t_return_value) - { - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(), - false, - nullptr, - t_return_value) - ; - } - - template - static auto get(const std::shared_ptr *obj, bool t_return_value) - { - return get(*obj, t_return_value); - } - - template - static auto get(const std::shared_ptr &obj, bool t_return_value) - { - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(obj), - false, - obj.get(), - t_return_value - ); - } - - template - static auto get(std::shared_ptr &&obj, bool t_return_value) - { - auto ptr = obj.get(); - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(std::move(obj)), - false, - ptr, - t_return_value - ); - } - - - - template - static auto get(T *t, bool t_return_value) - { - return get(std::ref(*t), t_return_value); - } - - template - static auto get(const T *t, bool t_return_value) - { - return get(std::cref(*t), t_return_value); - } - - - template - static auto get(std::reference_wrapper obj, bool t_return_value) - { - auto p = &obj.get(); - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(std::move(obj)), - true, - p, - t_return_value - ); - } - - template - static auto get(std::unique_ptr &&obj, bool t_return_value) - { - auto ptr = obj.get(); - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(std::make_shared>(std::move(obj))), - true, - ptr, - t_return_value - ); - } - - template - static auto get(T t, bool t_return_value) - { - auto p = std::make_shared(std::move(t)); - auto ptr = p.get(); - return std::make_shared( - detail::Get_Type_Info::get(), - chaiscript::detail::Any(std::move(p)), - false, - ptr, - t_return_value - ); - } - - static std::shared_ptr get() - { - return std::make_shared( - Type_Info(), - chaiscript::detail::Any(), - false, - nullptr, - false - ); - } - - }; - - public: - /// Basic Boxed_Value constructor - template::type>::value>::type> - explicit Boxed_Value(T &&t, bool t_return_value = false) - : m_data(Object_Data::get(std::forward(t), t_return_value)) - { - } - - /// Unknown-type constructor - Boxed_Value() = default; - - Boxed_Value(Boxed_Value&&) = default; - Boxed_Value& operator=(Boxed_Value&&) = default; - Boxed_Value(const Boxed_Value&) = default; - Boxed_Value& operator=(const Boxed_Value&) = default; - - void swap(Boxed_Value &rhs) - { - std::swap(m_data, rhs.m_data); + private: + /// structure which holds the internal state of a Boxed_Value + /// \todo Get rid of Any and merge it with this, reducing an allocation in the process + struct Data { + Data(const Type_Info &ti, chaiscript::detail::Any to, bool is_ref, const void *t_void_ptr, bool t_return_value) + : m_type_info(ti) + , m_obj(std::move(to)) + , m_data_ptr(ti.is_const() ? nullptr : const_cast(t_void_ptr)) + , m_const_data_ptr(t_void_ptr) + , m_is_ref(is_ref) + , m_return_value(t_return_value) { } - /// Copy the values stored in rhs.m_data to m_data. - /// m_data pointers are not shared in this case - Boxed_Value assign(const Boxed_Value &rhs) - { - (*m_data) = (*rhs.m_data); + Data &operator=(const Data &rhs) { + m_type_info = rhs.m_type_info; + m_obj = rhs.m_obj; + m_is_ref = rhs.m_is_ref; + m_data_ptr = rhs.m_data_ptr; + m_const_data_ptr = rhs.m_const_data_ptr; + m_return_value = rhs.m_return_value; + + if (rhs.m_attrs) { + m_attrs = std::make_unique>>(*rhs.m_attrs); + } + return *this; } - const Type_Info &get_type_info() const noexcept - { - return m_data->m_type_info; - } + Data(const Data &) = delete; - /// return true if the object is uninitialized - bool is_undef() const noexcept - { - return m_data->m_type_info.is_undef(); - } + Data(Data &&) = default; + Data &operator=(Data &&rhs) = default; - bool is_const() const noexcept - { - return m_data->m_type_info.is_const(); - } + Type_Info m_type_info; + chaiscript::detail::Any m_obj; + void *m_data_ptr; + const void *m_const_data_ptr; + std::unique_ptr>> m_attrs; + bool m_is_ref; + bool m_return_value; + }; - bool is_type(const Type_Info &ti) const noexcept - { - return m_data->m_type_info.bare_equal(ti); + struct Object_Data { + static auto get(Boxed_Value::Void_Type, bool t_return_value) { + return std::make_shared(detail::Get_Type_Info::get(), chaiscript::detail::Any(), false, nullptr, t_return_value); } - template - auto pointer_sentinel(std::shared_ptr &ptr) const - { - struct Sentinel { - Sentinel(std::shared_ptr &t_ptr, Data &data) - : m_ptr(t_ptr), m_data(data) - { - } - - ~Sentinel() - { - // save new pointer data - const auto ptr_ = m_ptr.get().get(); - m_data.get().m_data_ptr = ptr_; - m_data.get().m_const_data_ptr = ptr_; - } - - Sentinel& operator=(Sentinel&&s) = default; - Sentinel(Sentinel &&s) = default; - - operator std::shared_ptr&() const - { - return m_ptr.get(); - } - - Sentinel &operator=(const Sentinel &) = delete; - Sentinel(Sentinel&) = delete; - - std::reference_wrapper> m_ptr; - std::reference_wrapper m_data; - }; - - return Sentinel(ptr, *(m_data.get())); + static auto get(const std::shared_ptr *obj, bool t_return_value) { + return get(*obj, t_return_value); } - bool is_null() const noexcept - { - return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr); + template + static auto get(const std::shared_ptr &obj, bool t_return_value) { + return std::make_shared(detail::Get_Type_Info::get(), chaiscript::detail::Any(obj), false, obj.get(), t_return_value); } - const chaiscript::detail::Any & get() const noexcept - { - return m_data->m_obj; + template + static auto get(std::shared_ptr &&obj, bool t_return_value) { + auto ptr = obj.get(); + return std::make_shared(detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(obj)), false, ptr, t_return_value); } - bool is_ref() const noexcept - { - return m_data->m_is_ref; + template + static auto get(T *t, bool t_return_value) { + return get(std::ref(*t), t_return_value); } - bool is_return_value() const noexcept - { - return m_data->m_return_value; + template + static auto get(const T *t, bool t_return_value) { + return get(std::cref(*t), t_return_value); } - void reset_return_value() const noexcept - { - m_data->m_return_value = false; + template + static auto get(std::reference_wrapper obj, bool t_return_value) { + auto p = &obj.get(); + return std::make_shared(detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(obj)), true, p, t_return_value); } - bool is_pointer() const noexcept - { - return !is_ref(); + template + static auto get(std::unique_ptr &&obj, bool t_return_value) { + auto ptr = obj.get(); + return std::make_shared(detail::Get_Type_Info::get(), + chaiscript::detail::Any(std::make_shared>(std::move(obj))), + true, + ptr, + t_return_value); } - void *get_ptr() const noexcept - { - return m_data->m_data_ptr; + template + static auto get(T t, bool t_return_value) { + auto p = std::make_shared(std::move(t)); + auto ptr = p.get(); + return std::make_shared(detail::Get_Type_Info::get(), chaiscript::detail::Any(std::move(p)), false, ptr, t_return_value); } - const void *get_const_ptr() const noexcept - { - return m_data->m_const_data_ptr; - } + static std::shared_ptr get() { return std::make_shared(Type_Info(), chaiscript::detail::Any(), false, nullptr, false); } + }; - Boxed_Value get_attr(const std::string &t_name) - { - if (!m_data->m_attrs) - { - m_data->m_attrs = std::make_unique>>(); + public: + /// Basic Boxed_Value constructor + template>>> + explicit Boxed_Value(T &&t, bool t_return_value = false) + : m_data(Object_Data::get(std::forward(t), t_return_value)) { + } + + /// Unknown-type constructor + Boxed_Value() = default; + + Boxed_Value(Boxed_Value &&) = default; + Boxed_Value &operator=(Boxed_Value &&) = default; + Boxed_Value(const Boxed_Value &) = default; + Boxed_Value &operator=(const Boxed_Value &) = default; + + void swap(Boxed_Value &rhs) noexcept { std::swap(m_data, rhs.m_data); } + + /// Copy the values stored in rhs.m_data to m_data. + /// m_data pointers are not shared in this case + Boxed_Value assign(const Boxed_Value &rhs) noexcept { + (*m_data) = (*rhs.m_data); + return *this; + } + + const Type_Info &get_type_info() const noexcept { return m_data->m_type_info; } + + /// return true if the object is uninitialized + bool is_undef() const noexcept { return m_data->m_type_info.is_undef(); } + + bool is_const() const noexcept { return m_data->m_type_info.is_const(); } + + bool is_type(const Type_Info &ti) const noexcept { return m_data->m_type_info.bare_equal(ti); } + + template + auto pointer_sentinel(std::shared_ptr &ptr) const noexcept { + struct Sentinel { + Sentinel(std::shared_ptr &t_ptr, Data &data) + : m_ptr(t_ptr) + , m_data(data) { } - auto &attr = (*m_data->m_attrs)[t_name]; - if (attr) { - return Boxed_Value(attr, Internal_Construction()); - } else { - Boxed_Value bv; //default construct a new one - attr = bv.m_data; - return bv; + ~Sentinel() { + // save new pointer data + const auto ptr_ = m_ptr.get().get(); + m_data.get().m_data_ptr = ptr_; + m_data.get().m_const_data_ptr = ptr_; } + + Sentinel &operator=(Sentinel &&s) = default; + Sentinel(Sentinel &&s) = default; + + operator std::shared_ptr &() const noexcept { return m_ptr.get(); } + + Sentinel &operator=(const Sentinel &) = delete; + Sentinel(Sentinel &) = delete; + + std::reference_wrapper> m_ptr; + std::reference_wrapper m_data; + }; + + return Sentinel(ptr, *(m_data.get())); + } + + bool is_null() const noexcept { return (m_data->m_data_ptr == nullptr && m_data->m_const_data_ptr == nullptr); } + + const chaiscript::detail::Any &get() const noexcept { return m_data->m_obj; } + + bool is_ref() const noexcept { return m_data->m_is_ref; } + + bool is_return_value() const noexcept { return m_data->m_return_value; } + + void reset_return_value() const noexcept { m_data->m_return_value = false; } + + bool is_pointer() const noexcept { return !is_ref(); } + + void *get_ptr() const noexcept { return m_data->m_data_ptr; } + + const void *get_const_ptr() const noexcept { return m_data->m_const_data_ptr; } + + Boxed_Value get_attr(const std::string &t_name) { + if (!m_data->m_attrs) { + m_data->m_attrs = std::make_unique>>(); } - Boxed_Value ©_attrs(const Boxed_Value &t_obj) - { - if (t_obj.m_data->m_attrs) - { - m_data->m_attrs = std::make_unique>>(*t_obj.m_data->m_attrs); - } - return *this; + auto &attr = (*m_data->m_attrs)[t_name]; + if (attr) { + return Boxed_Value(attr, Internal_Construction()); + } else { + Boxed_Value bv; // default construct a new one + attr = bv.m_data; + return bv; } + } - Boxed_Value &clone_attrs(const Boxed_Value &t_obj) - { - copy_attrs(t_obj); - reset_return_value(); - return *this; + Boxed_Value ©_attrs(const Boxed_Value &t_obj) { + if (t_obj.m_data->m_attrs) { + m_data->m_attrs = std::make_unique>>(*t_obj.m_data->m_attrs); } + return *this; + } + Boxed_Value &clone_attrs(const Boxed_Value &t_obj) { + copy_attrs(t_obj); + reset_return_value(); + return *this; + } - /// \returns true if the two Boxed_Values share the same internal type - static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept - { - return l.get_type_info() == r.get_type_info(); - } + /// \returns true if the two Boxed_Values share the same internal type + static bool type_match(const Boxed_Value &l, const Boxed_Value &r) noexcept { return l.get_type_info() == r.get_type_info(); } - private: - // necessary to avoid hitting the templated && constructor of Boxed_Value - struct Internal_Construction{}; + private: + // necessary to avoid hitting the templated && constructor of Boxed_Value + struct Internal_Construction { + }; - Boxed_Value(std::shared_ptr t_data, Internal_Construction) + Boxed_Value(std::shared_ptr t_data, Internal_Construction) : m_data(std::move(t_data)) { - } + } - std::shared_ptr m_data = Object_Data::get(); + std::shared_ptr m_data = Object_Data::get(); }; - /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or std::reference_type + /// @brief Creates a Boxed_Value. If the object passed in is a value type, it is copied. If it is a pointer, std::shared_ptr, or + /// std::reference_type /// a copy is not made. /// @param t The value to box /// @@ -384,21 +264,19 @@ namespace chaiscript /// /// @sa @ref adding_objects template - Boxed_Value var(T &&t) - { - return Boxed_Value(std::forward(t)); - } + Boxed_Value var(T &&t) { + return Boxed_Value(std::forward(t)); + } namespace detail { /// \brief Takes a value, copies it and returns a Boxed_Value object that is immutable /// \param[in] t Value to copy and make const - /// \returns Immutable Boxed_Value + /// \returns Immutable Boxed_Value /// \sa Boxed_Value::is_const template - Boxed_Value const_var_impl(const T &t) - { - return Boxed_Value(std::make_shared::type >(t)); - } + Boxed_Value const_var_impl(const T &t) { + return Boxed_Value(std::make_shared::type>(t)); + } /// \brief Takes a pointer to a value, adds const to the pointed to type and returns an immutable Boxed_Value. /// Does not copy the pointed to value. @@ -406,10 +284,9 @@ namespace chaiscript /// \returns Immutable Boxed_Value /// \sa Boxed_Value::is_const template - Boxed_Value const_var_impl(T *t) - { - return Boxed_Value( const_cast::type *>(t) ); - } + Boxed_Value const_var_impl(T *t) { + return Boxed_Value(const_cast::type *>(t)); + } /// \brief Takes a std::shared_ptr to a value, adds const to the pointed to type and returns an immutable Boxed_Value. /// Does not copy the pointed to value. @@ -417,10 +294,9 @@ namespace chaiscript /// \returns Immutable Boxed_Value /// \sa Boxed_Value::is_const template - Boxed_Value const_var_impl(const std::shared_ptr &t) - { - return Boxed_Value( std::const_pointer_cast::type>(t) ); - } + Boxed_Value const_var_impl(const std::shared_ptr &t) { + return Boxed_Value(std::const_pointer_cast::type>(t)); + } /// \brief Takes a std::reference_wrapper value, adds const to the referenced type and returns an immutable Boxed_Value. /// Does not copy the referenced value. @@ -428,11 +304,10 @@ namespace chaiscript /// \returns Immutable Boxed_Value /// \sa Boxed_Value::is_const template - Boxed_Value const_var_impl(const std::reference_wrapper &t) - { - return Boxed_Value( std::cref(t.get()) ); - } - } + Boxed_Value const_var_impl(const std::reference_wrapper &t) { + return Boxed_Value(std::cref(t.get())); + } + } // namespace detail /// \brief Takes an object and returns an immutable Boxed_Value. If the object is a std::reference or pointer type /// the value is not copied. If it is an object type, it is copied. @@ -454,14 +329,13 @@ namespace chaiscript /// chai.add(chaiscript::const_var(Red), "Red"); /// chai.add(chaiscript::const_var(Green), "Green"); /// \endcode - /// + /// /// \todo support C++11 strongly typed enums /// \sa \ref adding_objects template - Boxed_Value const_var(const T &t) - { - return detail::const_var_impl(t); - } + Boxed_Value const_var(const T &t) { + return detail::const_var_impl(t); + } inline Boxed_Value void_var() { static const auto v = Boxed_Value(Boxed_Value::Void_Type()); @@ -479,7 +353,6 @@ namespace chaiscript } } -} +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/callable_traits.hpp b/include/chaiscript/dispatchkit/callable_traits.hpp index 4b61b088..3f8fa15c 100644 --- a/include/chaiscript/dispatchkit/callable_traits.hpp +++ b/include/chaiscript/dispatchkit/callable_traits.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_CALLABLE_TRAITS_HPP_ @@ -12,49 +12,50 @@ namespace chaiscript { namespace dispatch { namespace detail { - - template - struct Constructor - { - template - std::shared_ptr operator()(Inner&& ... inner) const { + template + struct Constructor { + template + std::shared_ptr operator()(Inner &&...inner) const { return std::make_shared(std::forward(inner)...); } }; - template - struct Const_Caller - { - explicit Const_Caller(Ret (Class::*t_func)(Param...) const) : m_func(t_func) {} + template + struct Const_Caller { + explicit Const_Caller(Ret (Class::*t_func)(Param...) const) + : m_func(t_func) { + } - template - Ret operator()(const Class &o, Inner&& ... inner) const { + template + Ret operator()(const Class &o, Inner &&...inner) const { return (o.*m_func)(std::forward(inner)...); } Ret (Class::*m_func)(Param...) const; }; - template - struct Fun_Caller - { - explicit Fun_Caller(Ret( * t_func)(Param...) ) : m_func(t_func) {} + template + struct Fun_Caller { + explicit Fun_Caller(Ret (*t_func)(Param...)) + : m_func(t_func) { + } - template - Ret operator()(Inner&& ... inner) const { + template + Ret operator()(Inner &&...inner) const { return (m_func)(std::forward(inner)...); } - Ret(*m_func)(Param...); + Ret (*m_func)(Param...); }; - template - struct Caller - { - explicit Caller(Ret (Class::*t_func)(Param...)) : m_func(t_func) {} + template + struct Caller { + explicit Caller(Ret (Class::*t_func)(Param...)) + : m_func(t_func) { + } - template - Ret operator()(Class &o, Inner&& ... inner) const { + template + Ret operator()(Class &o, Inner &&...inner) const { return (o.*m_func)(std::forward(inner)...); } @@ -62,46 +63,37 @@ namespace chaiscript { }; template - struct Arity - { - }; - - template - struct Arity - { - static const size_t arity = sizeof...(Params); - }; + struct Arity { + }; + template + struct Arity { + static const size_t arity = sizeof...(Params); + }; template - struct Function_Signature - { - }; + struct Function_Signature { + }; - template - struct Function_Signature - { - typedef Ret Return_Type; - typedef Ret (Signature)(Params...); - }; - - template - struct Function_Signature - { - typedef Ret Return_Type; - typedef Ret (Signature)(Params...); - }; + template + struct Function_Signature { + using Return_Type = Ret; + using Signature = Ret()(Params...); + }; + template + struct Function_Signature { + using Return_Type = Ret; + using Signature = Ret()(Params...); + }; template - struct Callable_Traits - { - typedef typename Function_Signature::Signature Signature; - typedef typename Function_Signature::Return_Type Return_Type; - }; - } - } -} + struct Callable_Traits { + using Signature = typename Function_Signature::Signature; + using Return_Type = typename Function_Signature::Return_Type; + }; + } // namespace detail + } // namespace dispatch +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 05f02535..06e8992a 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_DISPATCHKIT_HPP_ #define CHAISCRIPT_DISPATCHKIT_HPP_ @@ -19,412 +18,330 @@ #include #include #include +#include #include #include #include #include "../chaiscript_defines.hpp" #include "../chaiscript_threading.hpp" +#include "../utility/quick_flat_map.hpp" #include "bad_boxed_cast.hpp" #include "boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" -#include "type_conversions.hpp" #include "dynamic_object.hpp" #include "proxy_constructors.hpp" #include "proxy_functions.hpp" -#include "type_info.hpp" #include "short_alloc.hpp" +#include "type_conversions.hpp" +#include "type_info.hpp" namespace chaiscript { -class Boxed_Number; -} // namespace chaiscript + class Boxed_Number; +} // namespace chaiscript namespace chaiscript { namespace parser { class ChaiScript_Parser_Base; } -namespace dispatch { -class Dynamic_Proxy_Function; -class Proxy_Function_Base; -struct Placeholder_Object; -} // namespace dispatch -} // namespace chaiscript - - + namespace dispatch { + class Dynamic_Proxy_Function; + class Proxy_Function_Base; + struct Placeholder_Object; + } // namespace dispatch +} // namespace chaiscript /// \namespace chaiscript::dispatch /// \brief Classes and functions specific to the runtime dispatch side of ChaiScript. Some items may be of use to the end user. -namespace chaiscript -{ - namespace exception - { +namespace chaiscript { + namespace exception { /// Exception thrown in the case that an object name is invalid because it is a reserved word - class reserved_word_error : public std::runtime_error - { - public: - explicit reserved_word_error(const std::string &t_word) noexcept - : std::runtime_error("Reserved word not allowed in object name: " + t_word), m_word(t_word) - { - } + class reserved_word_error : public std::runtime_error { + public: + explicit reserved_word_error(const std::string &t_word) noexcept + : std::runtime_error("Reserved word not allowed in object name: " + t_word) + , m_word(t_word) { + } - reserved_word_error(const reserved_word_error &) = default; + reserved_word_error(const reserved_word_error &) = default; - ~reserved_word_error() noexcept override = default; + ~reserved_word_error() noexcept override = default; - std::string word() const - { - return m_word; - } + std::string word() const { return m_word; } - private: - std::string m_word; + private: + std::string m_word; }; /// Exception thrown in the case that an object name is invalid because it contains illegal characters - class illegal_name_error : public std::runtime_error - { - public: - explicit illegal_name_error(const std::string &t_name) noexcept - : std::runtime_error("Reserved name not allowed in object name: " + t_name), m_name(t_name) - { - } + class illegal_name_error : public std::runtime_error { + public: + explicit illegal_name_error(const std::string &t_name) noexcept + : std::runtime_error("Reserved name not allowed in object name: " + t_name) + , m_name(t_name) { + } - illegal_name_error(const illegal_name_error &) = default; + illegal_name_error(const illegal_name_error &) = default; - ~illegal_name_error() noexcept override = default; + ~illegal_name_error() noexcept override = default; - std::string name() const - { - return m_name; - } + std::string name() const { return m_name; } - private: - std::string m_name; + private: + std::string m_name; }; - /// Exception thrown in the case that an object name is invalid because it already exists in current context - class name_conflict_error : public std::runtime_error - { - public: - explicit name_conflict_error(const std::string &t_name) noexcept - : std::runtime_error("Name already exists in current context " + t_name), m_name(t_name) - { - } + class name_conflict_error : public std::runtime_error { + public: + explicit name_conflict_error(const std::string &t_name) noexcept + : std::runtime_error("Name already exists in current context " + t_name) + , m_name(t_name) { + } - name_conflict_error(const name_conflict_error &) = default; + name_conflict_error(const name_conflict_error &) = default; - ~name_conflict_error() noexcept override = default; + ~name_conflict_error() noexcept override = default; - std::string name() const - { - return m_name; - } - - private: - std::string m_name; + std::string name() const { return m_name; } + private: + std::string m_name; }; - /// Exception thrown in the case that a non-const object was added as a shared object - class global_non_const : public std::runtime_error - { - public: - global_non_const() noexcept - : std::runtime_error("a global object must be const") - { - } + class global_non_const : public std::runtime_error { + public: + global_non_const() noexcept + : std::runtime_error("a global object must be const") { + } - global_non_const(const global_non_const &) = default; - ~global_non_const() noexcept override = default; + global_non_const(const global_non_const &) = default; + ~global_non_const() noexcept override = default; }; - } - + } // namespace exception /// \brief Holds a collection of ChaiScript settings which can be applied to the ChaiScript runtime. /// Used to implement loadable module support. - class Module - { - public: - Module &add(Type_Info ti, std::string name) - { - m_typeinfos.emplace_back(ti, std::move(name)); - return *this; + class Module { + public: + Module &add(Type_Info ti, std::string name) { + m_typeinfos.emplace_back(ti, std::move(name)); + return *this; + } + + Module &add(Type_Conversion d) { + m_conversions.push_back(std::move(d)); + return *this; + } + + Module &add(Proxy_Function f, std::string name) { + m_funcs.emplace_back(std::move(f), std::move(name)); + return *this; + } + + Module &add_global_const(Boxed_Value t_bv, std::string t_name) { + if (!t_bv.is_const()) { + throw chaiscript::exception::global_non_const(); } - Module &add(Type_Conversion d) - { - m_conversions.push_back(std::move(d)); - return *this; + m_globals.emplace_back(std::move(t_bv), std::move(t_name)); + return *this; + } + + // Add a bit of ChaiScript to eval during module implementation + Module &eval(std::string str) { + m_evals.push_back(std::move(str)); + return *this; + } + + template + void apply(Eval &t_eval, Engine &t_engine) const { + apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine); + apply(m_funcs.begin(), m_funcs.end(), t_engine); + apply_eval(m_evals.begin(), m_evals.end(), t_eval); + apply_single(m_conversions.begin(), m_conversions.end(), t_engine); + apply_globals(m_globals.begin(), m_globals.end(), t_engine); + } + + bool has_function(const Proxy_Function &new_f, std::string_view name) noexcept { + return std::any_of(m_funcs.begin(), m_funcs.end(), [&](const std::pair &existing_f) { + return existing_f.second == name && *(existing_f.first) == *(new_f); + }); + } + + private: + std::vector> m_typeinfos; + std::vector> m_funcs; + std::vector> m_globals; + std::vector m_evals; + std::vector m_conversions; + + template + static void apply(InItr begin, const InItr end, T &t) { + for_each(begin, end, [&t](const auto &obj) { + try { + t.add(obj.first, obj.second); + } catch (const chaiscript::exception::name_conflict_error &) { + /// \todo Should we throw an error if there's a name conflict + /// while applying a module? + } + }); + } + + template + static void apply_globals(InItr begin, InItr end, T &t) { + while (begin != end) { + t.add_global_const(begin->first, begin->second); + ++begin; } + } - Module &add(Proxy_Function f, std::string name) - { - m_funcs.emplace_back(std::move(f), std::move(name)); - return *this; + template + static void apply_single(InItr begin, InItr end, T &t) { + while (begin != end) { + t.add(*begin); + ++begin; } + } - Module &add_global_const(Boxed_Value t_bv, std::string t_name) - { - if (!t_bv.is_const()) - { - throw chaiscript::exception::global_non_const(); - } - - m_globals.emplace_back(std::move(t_bv), std::move(t_name)); - return *this; + template + static void apply_eval(InItr begin, InItr end, T &t) { + while (begin != end) { + t.eval(*begin); + ++begin; } - - - //Add a bit of ChaiScript to eval during module implementation - Module &eval(const std::string &str) - { - m_evals.push_back(str); - return *this; - } - - template - void apply(Eval &t_eval, Engine &t_engine) const - { - apply(m_typeinfos.begin(), m_typeinfos.end(), t_engine); - apply(m_funcs.begin(), m_funcs.end(), t_engine); - apply_eval(m_evals.begin(), m_evals.end(), t_eval); - apply_single(m_conversions.begin(), m_conversions.end(), t_engine); - apply_globals(m_globals.begin(), m_globals.end(), t_engine); - } - - bool has_function(const Proxy_Function &new_f, const std::string &name) - { - return std::any_of(m_funcs.begin(), m_funcs.end(), - [&](const std::pair &existing_f) { - return existing_f.second == name && *(existing_f.first) == *(new_f); - } - ); - } - - - private: - std::vector> m_typeinfos; - std::vector> m_funcs; - std::vector> m_globals; - std::vector m_evals; - std::vector m_conversions; - - template - static void apply(InItr begin, const InItr end, T &t) - { - for_each(begin, end, - [&t](const auto &obj) { - try { - t.add(obj.first, obj.second); - } catch (const chaiscript::exception::name_conflict_error &) { - /// \todo Should we throw an error if there's a name conflict - /// while applying a module? - } - } - ); - } - - template - static void apply_globals(InItr begin, InItr end, T &t) - { - while (begin != end) - { - t.add_global_const(begin->first, begin->second); - ++begin; - } - } - - template - static void apply_single(InItr begin, InItr end, T &t) - { - while (begin != end) - { - t.add(*begin); - ++begin; - } - } - - template - static void apply_eval(InItr begin, InItr end, T &t) - { - while (begin != end) - { - t.eval(*begin); - ++begin; - } - } + } }; /// Convenience typedef for Module objects to be added to the ChaiScript runtime - typedef std::shared_ptr ModulePtr; + using ModulePtr = std::shared_ptr; - namespace detail - { + namespace detail { /// A Proxy_Function implementation that is able to take - /// a vector of Proxy_Functions and perform a dispatch on them. It is + /// a vector of Proxy_Functions and perform a dispatch on them. It is /// used specifically in the case of dealing with Function object variables - class Dispatch_Function final : public dispatch::Proxy_Function_Base - { - public: - explicit Dispatch_Function(std::vector t_funcs) - : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)), - m_funcs(std::move(t_funcs)) - { + class Dispatch_Function final : public dispatch::Proxy_Function_Base { + public: + explicit Dispatch_Function(std::vector t_funcs) + : Proxy_Function_Base(build_type_infos(t_funcs), calculate_arity(t_funcs)) + , m_funcs(std::move(t_funcs)) { + } + + bool operator==(const dispatch::Proxy_Function_Base &rhs) const noexcept override { + try { + const auto &dispatch_fun = dynamic_cast(rhs); + return m_funcs == dispatch_fun.m_funcs; + } catch (const std::bad_cast &) { + return false; + } + } + + std::vector get_contained_functions() const override { + return std::vector(m_funcs.begin(), m_funcs.end()); + } + + static int calculate_arity(const std::vector &t_funcs) noexcept { + if (t_funcs.empty()) { + return -1; } - bool operator==(const dispatch::Proxy_Function_Base &rhs) const override - { - try { - const auto &dispatch_fun = dynamic_cast(rhs); - return m_funcs == dispatch_fun.m_funcs; - } catch (const std::bad_cast &) { - return false; - } - } + const auto arity = t_funcs.front()->get_arity(); - std::vector get_contained_functions() const override - { - return std::vector(m_funcs.begin(), m_funcs.end()); - } - - - static int calculate_arity(const std::vector &t_funcs) - { - if (t_funcs.empty()) { + for (const auto &func : t_funcs) { + if (arity != func->get_arity()) { + // The arities in the list do not match, so it's unspecified return -1; } + } - const auto arity = t_funcs.front()->get_arity(); + return arity; + } - for (const auto &func : t_funcs) - { - if (arity != func->get_arity()) - { - // The arities in the list do not match, so it's unspecified - return -1; + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { + return std::any_of(std::begin(m_funcs), std::end(m_funcs), [&vals, &t_conversions](const Proxy_Function &f) { + return f->call_match(vals, t_conversions); + }); + } + + protected: + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + return dispatch::dispatch(m_funcs, params, t_conversions); + } + + private: + std::vector m_funcs; + + static std::vector build_type_infos(const std::vector &t_funcs) { + auto begin = t_funcs.cbegin(); + const auto &end = t_funcs.cend(); + + if (begin != end) { + std::vector type_infos = (*begin)->get_param_types(); + + ++begin; + + bool size_mismatch = false; + + while (begin != end) { + std::vector param_types = (*begin)->get_param_types(); + + if (param_types.size() != type_infos.size()) { + size_mismatch = true; } - } - return arity; - } - - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return std::any_of(std::begin(m_funcs), std::end(m_funcs), - [&vals, &t_conversions](const Proxy_Function &f){ return f->call_match(vals, t_conversions); }); - } - - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - return dispatch::dispatch(m_funcs, params, t_conversions); - } - - private: - std::vector m_funcs; - - static std::vector build_type_infos(const std::vector &t_funcs) - { - auto begin = t_funcs.cbegin(); - const auto &end = t_funcs.cend(); - - if (begin != end) - { - std::vector type_infos = (*begin)->get_param_types(); + for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i) { + if (!(type_infos[i] == param_types[i])) { + type_infos[i] = detail::Get_Type_Info::get(); + } + } ++begin; - - bool size_mismatch = false; - - while (begin != end) - { - std::vector param_types = (*begin)->get_param_types(); - - if (param_types.size() != type_infos.size()) - { - size_mismatch = true; - } - - for (size_t i = 0; i < type_infos.size() && i < param_types.size(); ++i) - { - if (!(type_infos[i] == param_types[i])) - { - type_infos[i] = detail::Get_Type_Info::get(); - } - } - - ++begin; - } - - assert(!type_infos.empty() && " type_info vector size is < 0, this is only possible if something else is broken"); - - if (size_mismatch) - { - type_infos.resize(1); - } - - return type_infos; } - return std::vector(); + assert(!type_infos.empty() && " type_info vector size is < 0, this is only possible if something else is broken"); + + if (size_mismatch) { + type_infos.resize(1); + } + + return type_infos; } + + return std::vector(); + } }; - } + } // namespace detail - - namespace detail - { - struct Stack_Holder - { - //template + namespace detail { + struct Stack_Holder { + // template // using SmallVector = std::vector>; - template - using SmallVector = std::vector; - + template + using SmallVector = std::vector; - typedef SmallVector> Scope; - typedef SmallVector StackData; - typedef SmallVector Stacks; - typedef SmallVector Call_Param_List; - typedef SmallVector Call_Params; + using Scope = utility::QuickFlatMap; + using StackData = SmallVector; + using Stacks = SmallVector; + using Call_Param_List = SmallVector; + using Call_Params = SmallVector; - Stack_Holder() - { + Stack_Holder() { push_stack(); push_call_params(); } - void push_stack_data() - { + void push_stack_data() { stacks.back().emplace_back(); -// stacks.back().emplace_back(Scope(scope_allocator)); + // stacks.back().emplace_back(Scope(scope_allocator)); } - void push_stack() - { - stacks.emplace_back(1); -// stacks.emplace_back(StackData(1, Scope(scope_allocator), stack_data_allocator)); - } + void push_stack() { stacks.emplace_back(1); } - void push_call_params() - { - call_params.emplace_back(); -// call_params.emplace_back(Call_Param_List(call_param_list_allocator)); - } - - //Scope::allocator_type::arena_type scope_allocator; - //StackData::allocator_type::arena_type stack_data_allocator; - //Stacks::allocator_type::arena_type stacks_allocator; - //Call_Param_List::allocator_type::arena_type call_param_list_allocator; - //Call_Params::allocator_type::arena_type call_params_allocator; - -// Stacks stacks = Stacks(stacks_allocator); -// Call_Params call_params = Call_Params(call_params_allocator); + void push_call_params() { call_params.emplace_back(); } Stacks stacks; Call_Params call_params; @@ -434,521 +351,404 @@ namespace chaiscript /// Main class for the dispatchkit. Handles management /// of the object stack, functions and registered types. - class Dispatch_Engine - { + class Dispatch_Engine { + public: + using Type_Name_Map = std::map; + using Scope = utility::QuickFlatMap; + using StackData = Stack_Holder::StackData; - public: - typedef std::map Type_Name_Map; - typedef std::vector> Scope; - typedef Stack_Holder::StackData StackData; + struct State { + utility::QuickFlatMap>, str_equal> m_functions; + utility::QuickFlatMap m_function_objects; + utility::QuickFlatMap m_boxed_functions; + std::map m_global_objects; + Type_Name_Map m_types; + }; - struct State - { - std::vector>>> m_functions; - std::vector> m_function_objects; - std::vector> m_boxed_functions; - std::map m_global_objects; - Type_Name_Map m_types; + explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser) + : m_stack_holder() + , m_parser(parser) { + } + + /// \brief casts an object while applying any Dynamic_Conversion available + template + decltype(auto) boxed_cast(const Boxed_Value &bv) const { + Type_Conversions_State state(m_conversions, m_conversions.conversion_saves()); + return (chaiscript::boxed_cast(bv, &state)); + } + + /// Add a new conversion for upcasting to a base class + void add(const Type_Conversion &d) { m_conversions.add_conversion(d); } + + /// Add a new named Proxy_Function to the system + void add(const Proxy_Function &f, const std::string &name) { add_function(f, name); } + + /// Set the value of an object, by name. If the object + /// is not available in the current scope it is created + void add(Boxed_Value obj, const std::string &name) { + auto &stack = get_stack_data(); + + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) { + if (auto itr = stack_elem->find(name); itr != stack_elem->end()) { + itr->second = std::move(obj); + return; + } + } + + add_object(name, std::move(obj)); + } + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + Boxed_Value &add_get_object(std::string t_name, Boxed_Value obj, Stack_Holder &t_holder) { + auto &stack_elem = get_stack_data(t_holder).back(); + + if (auto result = stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); result.second) { + return result.first->second; + } else { + // insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); + } + } + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(std::string t_name, Boxed_Value obj, Stack_Holder &t_holder) { + auto &stack_elem = get_stack_data(t_holder).back(); + + if (auto result = stack_elem.insert(std::pair{std::move(t_name), std::move(obj)}); !result.second) { + // insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); + } + } + + /// Adds a named object to the current scope + /// \warning This version does not check the validity of the name + /// it is meant for internal use only + void add_object(const std::string &name, Boxed_Value obj) { add_object(name, std::move(obj), get_stack_holder()); } + + /// Adds a new global shared object, between all the threads + void add_global_const(const Boxed_Value &obj, const std::string &name) { + if (!obj.is_const()) { + throw chaiscript::exception::global_non_const(); + } + + chaiscript::detail::threading::unique_lock l(m_mutex); + + if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) { + throw chaiscript::exception::name_conflict_error(name); + } else { + m_state.m_global_objects.insert(std::make_pair(name, obj)); + } + } + + /// Adds a new global (non-const) shared object, between all the threads + Boxed_Value add_global_no_throw(Boxed_Value obj, std::string name) { + chaiscript::detail::threading::unique_lock l(m_mutex); + + return m_state.m_global_objects.insert(std::pair{std::move(name), std::move(obj)}).first->second; + } + + /// Adds a new global (non-const) shared object, between all the threads + void add_global(Boxed_Value obj, std::string name) { + chaiscript::detail::threading::unique_lock l(m_mutex); + + if (auto result = m_state.m_global_objects.insert(std::pair{std::move(name), std::move(obj)}); !result.second) { + // insert failed + throw chaiscript::exception::name_conflict_error(result.first->first); + } + } + + /// Updates an existing global shared object or adds a new global shared object if not found + void set_global(Boxed_Value obj, std::string name) { + chaiscript::detail::threading::unique_lock l(m_mutex); + m_state.m_global_objects.insert_or_assign(std::move(name), std::move(obj)); + } + + /// Adds a new scope to the stack + void new_scope() { new_scope(*m_stack_holder); } + + /// Pops the current scope from the stack + void pop_scope() { pop_scope(*m_stack_holder); } + + /// Adds a new scope to the stack + static void new_scope(Stack_Holder &t_holder) { + t_holder.push_stack_data(); + t_holder.push_call_params(); + } + + /// Pops the current scope from the stack + static void pop_scope(Stack_Holder &t_holder) { + t_holder.call_params.pop_back(); + StackData &stack = get_stack_data(t_holder); + + assert(!stack.empty()); + + stack.pop_back(); + } + + /// Pushes a new stack on to the list of stacks + static void new_stack(Stack_Holder &t_holder) { + // add a new Stack with 1 element + t_holder.push_stack(); + } + + static void pop_stack(Stack_Holder &t_holder) { t_holder.stacks.pop_back(); } + + /// Searches the current stack for an object of the given name + /// includes a special overload for the _ place holder object to + /// ensure that it is always in scope. + Boxed_Value get_object(std::string_view name, std::atomic_uint_fast32_t &t_loc, Stack_Holder &t_holder) const { + enum class Loc : uint_fast32_t { + located = 0x80000000, + is_local = 0x40000000, + stack_mask = 0x0FFF0000, + loc_mask = 0x0000FFFF }; - explicit Dispatch_Engine(chaiscript::parser::ChaiScript_Parser_Base &parser) - : m_stack_holder(), - m_parser(parser) - { - } + uint_fast32_t loc = t_loc; - /// \brief casts an object while applying any Dynamic_Conversion available - template - decltype(auto) boxed_cast(const Boxed_Value &bv) const - { - Type_Conversions_State state(m_conversions, m_conversions.conversion_saves()); - return(chaiscript::boxed_cast(bv, &state)); - } + if (loc == 0) { + auto &stack = get_stack_data(t_holder); - /// Add a new conversion for upcasting to a base class - void add(const Type_Conversion &d) - { - m_conversions.add_conversion(d); - } - - /// Add a new named Proxy_Function to the system - void add(const Proxy_Function &f, const std::string &name) - { - add_function(f, name); - } - - /// Set the value of an object, by name. If the object - /// is not available in the current scope it is created - void add(Boxed_Value obj, const std::string &name) - { - auto &stack = get_stack_data(); - - for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) - { - auto itr = std::find_if(stack_elem->begin(), stack_elem->end(), - [&](const std::pair &o) { - return o.first == name; - }); - - if (itr != stack_elem->end()) - { - itr->second = std::move(obj); - return; - } - } - - add_object(name, std::move(obj)); - } - - /// Adds a named object to the current scope - /// \warning This version does not check the validity of the name - /// it is meant for internal use only - Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder) - { - auto &stack_elem = get_stack_data(t_holder).back(); - - if (std::any_of(stack_elem.begin(), stack_elem.end(), - [&](const std::pair &o) { - return o.first == t_name; - })) - { - throw chaiscript::exception::name_conflict_error(t_name); - } - - stack_elem.emplace_back(t_name, std::move(obj)); - return stack_elem.back().second; - } - - - /// Adds a named object to the current scope - /// \warning This version does not check the validity of the name - /// it is meant for internal use only - void add_object(const std::string &t_name, Boxed_Value obj, Stack_Holder &t_holder) - { - auto &stack_elem = get_stack_data(t_holder).back(); - - if (std::any_of(stack_elem.begin(), stack_elem.end(), - [&](const std::pair &o) { - return o.first == t_name; - })) - { - throw chaiscript::exception::name_conflict_error(t_name); - } - - stack_elem.emplace_back(t_name, std::move(obj)); - } - - - /// Adds a named object to the current scope - /// \warning This version does not check the validity of the name - /// it is meant for internal use only - void add_object(const std::string &name, Boxed_Value obj) - { - add_object(name, std::move(obj), get_stack_holder()); - } - - /// Adds a new global shared object, between all the threads - void add_global_const(const Boxed_Value &obj, const std::string &name) - { - if (!obj.is_const()) - { - throw chaiscript::exception::global_non_const(); - } - - chaiscript::detail::threading::unique_lock l(m_mutex); - - if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) - { - throw chaiscript::exception::name_conflict_error(name); - } else { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - } - } - - /// Adds a new global (non-const) shared object, between all the threads - Boxed_Value add_global_no_throw(const Boxed_Value &obj, const std::string &name) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - - const auto itr = m_state.m_global_objects.find(name); - if (itr == m_state.m_global_objects.end()) - { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - return obj; - } else { - return itr->second; - } - } - - - /// Adds a new global (non-const) shared object, between all the threads - void add_global(const Boxed_Value &obj, const std::string &name) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - - if (m_state.m_global_objects.find(name) != m_state.m_global_objects.end()) - { - throw chaiscript::exception::name_conflict_error(name); - } else { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - } - } - - /// Updates an existing global shared object or adds a new global shared object if not found - void set_global(const Boxed_Value &obj, const std::string &name) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - - const auto itr = m_state.m_global_objects.find(name); - if (itr != m_state.m_global_objects.end()) - { - itr->second.assign(obj); - } else { - m_state.m_global_objects.insert(std::make_pair(name, obj)); - } - } - - /// Adds a new scope to the stack - void new_scope() - { - new_scope(*m_stack_holder); - } - - /// Pops the current scope from the stack - void pop_scope() - { - pop_scope(*m_stack_holder); - } - - /// Adds a new scope to the stack - static void new_scope(Stack_Holder &t_holder) - { - t_holder.push_stack_data(); - t_holder.push_call_params(); - } - - /// Pops the current scope from the stack - static void pop_scope(Stack_Holder &t_holder) - { - t_holder.call_params.pop_back(); - StackData &stack = get_stack_data(t_holder); - - assert(!stack.empty()); - - stack.pop_back(); - } - - - /// Pushes a new stack on to the list of stacks - static void new_stack(Stack_Holder &t_holder) - { - // add a new Stack with 1 element - t_holder.push_stack(); - } - - static void pop_stack(Stack_Holder &t_holder) - { - t_holder.stacks.pop_back(); - } - - /// Searches the current stack for an object of the given name - /// includes a special overload for the _ place holder object to - /// ensure that it is always in scope. - Boxed_Value get_object(const std::string &name, std::atomic_uint_fast32_t &t_loc, Stack_Holder &t_holder) const - { - enum class Loc : uint_fast32_t { - located = 0x80000000, - is_local = 0x40000000, - stack_mask = 0x0FFF0000, - loc_mask = 0x0000FFFF - }; - - uint_fast32_t loc = t_loc; - - if (loc == 0) - { - auto &stack = get_stack_data(t_holder); - - // Is it in the stack? - for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) - { - for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s ) - { - if (s->first == name) { - t_loc = static_cast(std::distance(stack.rbegin(), stack_elem) << 16) - | static_cast(std::distance(stack_elem->begin(), s)) - | static_cast(Loc::located) - | static_cast(Loc::is_local); - return s->second; - } + // Is it in the stack? + for (auto stack_elem = stack.rbegin(); stack_elem != stack.rend(); ++stack_elem) { + for (auto s = stack_elem->begin(); s != stack_elem->end(); ++s) { + if (s->first == name) { + t_loc = static_cast(std::distance(stack.rbegin(), stack_elem) << 16) + | static_cast(std::distance(stack_elem->begin(), s)) | static_cast(Loc::located) + | static_cast(Loc::is_local); + return s->second; } } - - t_loc = static_cast(Loc::located); - } else if ((loc & static_cast(Loc::is_local)) != 0u) { - auto &stack = get_stack_data(t_holder); - - return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)][loc & static_cast(Loc::loc_mask)].second; } - // Is the value we are looking for a global or function? - chaiscript::detail::threading::shared_lock l(m_mutex); - - const auto itr = m_state.m_global_objects.find(name); - if (itr != m_state.m_global_objects.end()) - { - return itr->second; - } - - // no? is it a function object? - auto obj = get_function_object_int(name, loc); - if (obj.first != loc) { t_loc = uint_fast32_t(obj.first); } - - return obj.second; + t_loc = static_cast(Loc::located); + } else if ((loc & static_cast(Loc::is_local)) != 0u) { + auto &stack = get_stack_data(t_holder); + return stack[stack.size() - 1 - ((loc & static_cast(Loc::stack_mask)) >> 16)].at_index( + loc & static_cast(Loc::loc_mask)); } - /// Registers a new named type - void add(const Type_Info &ti, const std::string &name) - { - add_global_const(const_var(ti), name + "_type"); + // Is the value we are looking for a global or function? + chaiscript::detail::threading::shared_lock l(m_mutex); - chaiscript::detail::threading::unique_lock l(m_mutex); - - m_state.m_types.insert(std::make_pair(name, ti)); + const auto itr = m_state.m_global_objects.find(name); + if (itr != m_state.m_global_objects.end()) { + return itr->second; } - /// Returns the type info for a named type - Type_Info get_type(const std::string &name, bool t_throw = true) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + // no? is it a function object? + auto obj = get_function_object_int(name, loc); + if (obj.first != loc) { + t_loc = uint_fast32_t(obj.first); + } - const auto itr = m_state.m_types.find(name); + return obj.second; + } - if (itr != m_state.m_types.end()) - { - return itr->second; - } + /// Registers a new named type + void add(const Type_Info &ti, const std::string &name) { + add_global_const(const_var(ti), name + "_type"); - if (t_throw) { - throw std::range_error("Type Not Known: " + name); - } else { - return Type_Info(); + chaiscript::detail::threading::unique_lock l(m_mutex); + + m_state.m_types.insert(std::make_pair(name, ti)); + } + + /// Returns the type info for a named type + Type_Info get_type(std::string_view name, bool t_throw = true) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto itr = m_state.m_types.find(name); + + if (itr != m_state.m_types.end()) { + return itr->second; + } + + if (t_throw) { + throw std::range_error("Type Not Known: " + std::string(name)); + } else { + return Type_Info(); + } + } + + /// Returns the registered name of a known type_info object + /// compares the "bare_type_info" for the broadest possible + /// match + std::string get_type_name(const Type_Info &ti) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + for (const auto &elem : m_state.m_types) { + if (elem.second.bare_equal(ti)) { + return elem.first; } } - /// Returns the registered name of a known type_info object - /// compares the "bare_type_info" for the broadest possible - /// match - std::string get_type_name(const Type_Info &ti) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + return ti.bare_name(); + } - for (const auto & elem : m_state.m_types) - { - if (elem.second.bare_equal(ti)) - { - return elem.first; - } - } + /// Return all registered types + std::vector> get_types() const { + chaiscript::detail::threading::shared_lock l(m_mutex); - return ti.bare_name(); + return std::vector>(m_state.m_types.begin(), m_state.m_types.end()); + } + + std::shared_ptr> get_method_missing_functions() const { + uint_fast32_t method_missing_loc = m_method_missing_loc; + auto method_missing_funs = get_function("method_missing", method_missing_loc); + if (method_missing_funs.first != method_missing_loc) { + m_method_missing_loc = uint_fast32_t(method_missing_funs.first); } - /// Return all registered types - std::vector > get_types() const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + return std::move(method_missing_funs.second); + } - return std::vector >(m_state.m_types.begin(), m_state.m_types.end()); + /// Return a function by name + std::pair>> get_function(std::string_view t_name, const size_t t_hint) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto &funs = get_functions_int(); + + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + return std::make_pair(size_t(0), std::make_shared>()); + } + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + Boxed_Value get_function_object(const std::string &t_name) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return get_function_object_int(t_name, 0).second; + } + + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + /// \warn does not obtain a mutex lock. \sa get_function_object for public version + std::pair get_function_object_int(std::string_view t_name, const size_t t_hint) const { + const auto &funs = get_boxed_functions_int(); + + if (const auto itr = funs.find(t_name, t_hint); itr != funs.end()) { + return std::make_pair(std::distance(funs.begin(), itr), itr->second); + } else { + throw std::range_error("Object not found: " + std::string(t_name)); + } + } + + /// Return true if a function exists + bool function_exists(std::string_view name) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return get_functions_int().count(name) > 0; + } + + /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, + /// the current scope. + std::map get_parent_locals() const { + auto &stack = get_stack_data(); + if (stack.size() > 1) { + return std::map(stack[1].begin(), stack[1].end()); + } else { + return std::map(stack[0].begin(), stack[0].end()); + } + } + + /// \returns All values in the local thread state, added through the add() function + std::map get_locals() const { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + return std::map(scope.begin(), scope.end()); + } + + /// \brief Sets all of the locals for the current thread state. + /// + /// \param[in] t_locals The map set of variables to replace the current state with + /// + /// Any existing locals are removed and the given set of variables is added + void set_locals(const std::map &t_locals) { + auto &stack = get_stack_data(); + auto &scope = stack.front(); + scope.assign(t_locals.begin(), t_locals.end()); + } + + /// + /// Get a map of all objects that can be seen from the current scope in a scripting context + /// + std::map get_scripting_objects() const { + const Stack_Holder &s = *m_stack_holder; + + // We don't want the current context, but one up if it exists + const StackData &stack = (s.stacks.size() == 1) ? (s.stacks.back()) : (s.stacks[s.stacks.size() - 2]); + + std::map retval; + + // note: map insert doesn't overwrite existing values, which is why this works + for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr) { + retval.insert(itr->begin(), itr->end()); } - std::shared_ptr> get_method_missing_functions() const - { - uint_fast32_t method_missing_loc = m_method_missing_loc; - auto method_missing_funs = get_function("method_missing", method_missing_loc); - if (method_missing_funs.first != method_missing_loc) { - m_method_missing_loc = uint_fast32_t(method_missing_funs.first); - } + // add the global values + chaiscript::detail::threading::shared_lock l(m_mutex); + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); - return std::move(method_missing_funs.second); + return retval; + } + + /// + /// Get a map of all functions that can be seen from a scripting context + /// + std::map get_function_objects() const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto &funs = get_function_objects_int(); + + std::map objs; + + for (const auto &fun : funs) { + objs.insert(std::make_pair(fun.first, const_var(fun.second))); } + return objs; + } - /// Return a function by name - std::pair>> get_function(const std::string &t_name, const size_t t_hint) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + /// Get a vector of all registered functions + std::vector> get_functions() const { + chaiscript::detail::threading::shared_lock l(m_mutex); - const auto &funs = get_functions_int(); + std::vector> rets; - auto itr = find_keyed_value(funs, t_name, t_hint); + const auto &functions = get_functions_int(); - if (itr != funs.end()) - { - return std::make_pair(std::distance(funs.begin(), itr), itr->second); - } else { - return std::make_pair(size_t(0), std::make_shared>()); + for (const auto &function : functions) { + for (const auto &internal_func : *function.second) { + rets.emplace_back(function.first, internal_func); } } - /// \returns a function object (Boxed_Value wrapper) if it exists - /// \throws std::range_error if it does not - Boxed_Value get_function_object(const std::string &t_name) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + return rets; + } - return get_function_object_int(t_name, 0).second; + const Type_Conversions &conversions() const noexcept { return m_conversions; } + + static bool is_attribute_call(const std::vector &t_funs, + const Function_Params &t_params, + bool t_has_params, + const Type_Conversions_State &t_conversions) noexcept { + if (!t_has_params || t_params.empty()) { + return false; } - /// \returns a function object (Boxed_Value wrapper) if it exists - /// \throws std::range_error if it does not - /// \warn does not obtain a mutex lock. \sa get_function_object for public version - std::pair get_function_object_int(const std::string &t_name, const size_t t_hint) const - { - const auto &funs = get_boxed_functions_int(); - - auto itr = find_keyed_value(funs, t_name, t_hint); - - if (itr != funs.end()) - { - return std::make_pair(std::distance(funs.begin(), itr), itr->second); - } else { - throw std::range_error("Object not found: " + t_name); - } - } - - - /// Return true if a function exists - bool function_exists(const std::string &name) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); - - const auto &functions = get_functions_int(); - return find_keyed_value(functions, name) != functions.end(); - } - - /// \returns All values in the local thread state in the parent scope, or if it doesn't exist, - /// the current scope. - std::map get_parent_locals() const - { - auto &stack = get_stack_data(); - if (stack.size() > 1) - { - return std::map(stack[1].begin(), stack[1].end()); - } else { - return std::map(stack[0].begin(), stack[0].end()); - } - } - - /// \returns All values in the local thread state, added through the add() function - std::map get_locals() const - { - auto &stack = get_stack_data(); - auto &scope = stack.front(); - return std::map(scope.begin(), scope.end()); - } - - /// \brief Sets all of the locals for the current thread state. - /// - /// \param[in] t_locals The map set of variables to replace the current state with - /// - /// Any existing locals are removed and the given set of variables is added - void set_locals(const std::map &t_locals) - { - auto &stack = get_stack_data(); - auto &scope = stack.front(); - scope.assign(t_locals.begin(), t_locals.end()); - } - - - - /// - /// Get a map of all objects that can be seen from the current scope in a scripting context - /// - std::map get_scripting_objects() const - { - const Stack_Holder &s = *m_stack_holder; - - // We don't want the current context, but one up if it exists - const StackData &stack = (s.stacks.size()==1)?(s.stacks.back()):(s.stacks[s.stacks.size()-2]); - - std::map retval; - - // note: map insert doesn't overwrite existing values, which is why this works - for (auto itr = stack.rbegin(); itr != stack.rend(); ++itr) - { - retval.insert(itr->begin(), itr->end()); - } - - // add the global values - chaiscript::detail::threading::shared_lock l(m_mutex); - retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); - - return retval; - } - - - /// - /// Get a map of all functions that can be seen from a scripting context - /// - std::map get_function_objects() const - { - chaiscript::detail::threading::shared_lock l(m_mutex); - - const auto &funs = get_function_objects_int(); - - std::map objs; - - for (const auto & fun : funs) - { - objs.insert(std::make_pair(fun.first, const_var(fun.second))); - } - - return objs; - } - - - /// Get a vector of all registered functions - std::vector > get_functions() const - { - chaiscript::detail::threading::shared_lock l(m_mutex); - - std::vector > rets; - - const auto &functions = get_functions_int(); - - for (const auto & function : functions) - { - for (const auto & internal_func : *function.second) - { - rets.emplace_back(function.first, internal_func); - } - } - - return rets; - } - - - const Type_Conversions &conversions() const - { - return m_conversions; - } - - static bool is_attribute_call(const std::vector &t_funs, const std::vector &t_params, - bool t_has_params, const Type_Conversions_State &t_conversions) - { - if (!t_has_params || t_params.empty()) { - return false; - } - - return std::any_of(std::begin(t_funs), std::end(t_funs), - [&](const auto &fun) { - return fun->is_attribute_function() && fun->compare_first_type(t_params[0], t_conversions); - } - ); - - } + return std::any_of(std::begin(t_funs), std::end(t_funs), [&](const auto &fun) { + return fun->is_attribute_function() && fun->compare_first_type(t_params[0], t_conversions); + }); + } #ifdef CHAISCRIPT_MSVC // MSVC is unable to recognize that "rethrow_exception" causes the function to return @@ -956,609 +756,449 @@ namespace chaiscript #pragma warning(push) #pragma warning(disable : 4715) #endif - Boxed_Value call_member(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, bool t_has_params, - const Type_Conversions_State &t_conversions) - { - uint_fast32_t loc = t_loc; - const auto funs = get_function(t_name, loc); - if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); } + Boxed_Value call_member(const std::string &t_name, + std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, + bool t_has_params, + const Type_Conversions_State &t_conversions) { + uint_fast32_t loc = t_loc; + const auto funs = get_function(t_name, loc); + if (funs.first != loc) { + t_loc = uint_fast32_t(funs.first); + } - const auto do_attribute_call = - [this](int l_num_params, const std::vector &l_params, const std::vector &l_funs, const Type_Conversions_State &l_conversions)->Boxed_Value - { - std::vector attr_params{l_params.begin(), l_params.begin() + 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())) { - struct This_Foist { - This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) : m_e(e) { - m_e.get().new_scope(); - m_e.get().add_object("__this", t_bv); - } - - ~This_Foist() { - m_e.get().pop_scope(); - } - - std::reference_wrapper m_e; - }; - - This_Foist fi(*this, l_params.front()); - - try { - auto func = boxed_cast(bv); - try { - return (*func)({l_params.begin() + l_num_params, l_params.end()}, 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()}, - 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()}, - std::vector(l_funs.begin(), l_funs.end())); - } - } else { - return bv; + const auto do_attribute_call = [this](int 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); + 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())) { + struct This_Foist { + This_Foist(Dispatch_Engine &e, const Boxed_Value &t_bv) + : m_e(e) { + m_e.get().new_scope(); + m_e.get().add_object("__this", t_bv); } + + ~This_Foist() { m_e.get().pop_scope(); } + + std::reference_wrapper m_e; }; - if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) { - return do_attribute_call(1, params, *funs.second, t_conversions); + This_Foist fi(*this, l_params.front()); + + try { + auto func = boxed_cast(bv); + try { + return (*func)({l_params.begin() + l_num_params, l_params.end()}, 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()}, + 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()}, + std::vector(l_funs.begin(), l_funs.end())); + } } else { - std::exception_ptr except; + return bv; + } + }; - if (!funs.second->empty()) { - try { - return dispatch::dispatch(*funs.second, params, t_conversions); - } catch(chaiscript::exception::dispatch_error&) { - except = std::current_exception(); - } - } + if (is_attribute_call(*funs.second, params, t_has_params, t_conversions)) { + return do_attribute_call(1, params, *funs.second, t_conversions); + } else { + std::exception_ptr except; - // If we get here we know that either there was no method with that name, - // or there was no matching method - - const auto functions = [&]()->std::vector { - std::vector fs; - - const auto method_missing_funs = get_method_missing_functions(); - - for (const auto &f : *method_missing_funs) - { - if(f->compare_first_type(params[0], t_conversions)) { - fs.push_back(f); - } - } - - return fs; - }(); - - - - const bool is_no_param = [&]()->bool{ - for (const auto &f : functions) { - if (f->get_arity() != 2) { - return false; - } - } - return true; - }(); - - if (!functions.empty()) { - try { - if (is_no_param) { - std::vector tmp_params(params); - tmp_params.insert(tmp_params.begin() + 1, var(t_name)); - return do_attribute_call(2, tmp_params, functions, t_conversions); - } else { - return dispatch::dispatch(functions, {params[0], var(t_name), var(std::vector(params.begin()+1, params.end()))}, t_conversions); - } - } catch (const dispatch::option_explicit_set &e) { - throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end()), - e.what()); - } - } - - // If we get all the way down here we know there was no "method_missing" - // method at all. - if (except) { - std::rethrow_exception(except); - } else { - throw chaiscript::exception::dispatch_error(params, std::vector(funs.second->begin(), funs.second->end())); + if (!funs.second->empty()) { + try { + return dispatch::dispatch(*funs.second, params, t_conversions); + } catch (chaiscript::exception::dispatch_error &) { + except = std::current_exception(); } } + + // If we get here we know that either there was no method with that name, + // or there was no matching method + + const auto functions = [&]() -> std::vector { + std::vector fs; + + const auto method_missing_funs = get_method_missing_functions(); + + for (const auto &f : *method_missing_funs) { + if (f->compare_first_type(params[0], t_conversions)) { + fs.push_back(f); + } + } + + return fs; + }(); + + const bool is_no_param = [&]() -> bool { + for (const auto &f : functions) { + if (f->get_arity() != 2) { + return false; + } + } + return true; + }(); + + if (!functions.empty()) { + try { + if (is_no_param) { + auto tmp_params = params.to_vector(); + tmp_params.insert(tmp_params.begin() + 1, var(t_name)); + return do_attribute_call(2, Function_Params(tmp_params), functions, t_conversions); + } else { + std::array p{params[0], var(t_name), var(std::vector(params.begin() + 1, params.end()))}; + return dispatch::dispatch(functions, Function_Params{p}, t_conversions); + } + } catch (const dispatch::option_explicit_set &e) { + throw chaiscript::exception::dispatch_error(params, + std::vector(funs.second->begin(), funs.second->end()), + e.what()); + } + } + + // If we get all the way down here we know there was no "method_missing" + // method at all. + if (except) { + std::rethrow_exception(except); + } else { + throw chaiscript::exception::dispatch_error(params, + std::vector(funs.second->begin(), funs.second->end())); + } } + } #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif + Boxed_Value call_function(std::string_view t_name, + std::atomic_uint_fast32_t &t_loc, + const Function_Params ¶ms, + const Type_Conversions_State &t_conversions) const { + uint_fast32_t loc = t_loc; + const auto [func_loc, func] = get_function(t_name, loc); + if (func_loc != loc) { + t_loc = uint_fast32_t(func_loc); + } + return dispatch::dispatch(*func, params, t_conversions); + } + /// Dump object info to stdout + void dump_object(const Boxed_Value &o) const { std::cout << (o.is_const() ? "const " : "") << type_name(o) << '\n'; } - Boxed_Value call_function(const std::string &t_name, std::atomic_uint_fast32_t &t_loc, const std::vector ¶ms, - const Type_Conversions_State &t_conversions) const - { - uint_fast32_t loc = t_loc; - const auto funs = get_function(t_name, loc); - if (funs.first != loc) { t_loc = uint_fast32_t(funs.first); -} - return dispatch::dispatch(*funs.second, params, t_conversions); + /// Dump type info to stdout + void dump_type(const Type_Info &type) const { std::cout << (type.is_const() ? "const " : "") << get_type_name(type); } + + /// Dump function to stdout + void dump_function(const std::pair &f) const { + const auto params = f.second->get_param_types(); + + dump_type(params.front()); + std::cout << " " << f.first << "("; + + for (auto itr = params.begin() + 1; itr != params.end();) { + dump_type(*itr); + ++itr; + + if (itr != params.end()) { + std::cout << ", "; + } } + std::cout << ") \n"; + } - /// Dump object info to stdout - void dump_object(const Boxed_Value &o) const - { - std::cout << (o.is_const()?"const ":"") << type_name(o) << '\n'; + /// Returns true if a call can be made that consists of the first parameter + /// (the function) with the remaining parameters as its arguments. + Boxed_Value call_exists(const Function_Params ¶ms) const { + if (params.empty()) { + throw chaiscript::exception::arity_error(static_cast(params.size()), 1); } - /// Dump type info to stdout - void dump_type(const Type_Info &type) const - { - std::cout << (type.is_const()?"const ":"") << get_type_name(type); + 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)); + } + + /// Dump all system info to stdout + void dump_system() const { + std::cout << "Registered Types: \n"; + for (const auto &[type_name, type] : get_types()) { + std::cout << type_name << ": " << type.bare_name() << '\n'; } - /// Dump function to stdout - void dump_function(const std::pair &f) const - { - std::vector params = f.second->get_param_types(); + std::cout << '\n'; - dump_type(params.front()); - std::cout << " " << f.first << "("; + std::cout << "Functions: \n"; + for (const auto &func : get_functions()) { + dump_function(func); + } + std::cout << '\n'; + } - for (std::vector::const_iterator itr = params.begin() + 1; - itr != params.end(); - ) - { - dump_type(*itr); - ++itr; - - if (itr != params.end()) - { - std::cout << ", "; - } + /// return true if the Boxed_Value matches the registered type by name + bool is_type(const Boxed_Value &r, std::string_view user_typename) const noexcept { + try { + if (get_type(user_typename).bare_equal(r.get_type_info())) { + return true; } - - std::cout << ") \n"; + } catch (const std::range_error &) { } - /// Returns true if a call can be made that consists of the first parameter - /// (the function) with the remaining parameters as its arguments. - Boxed_Value call_exists(const std::vector ¶ms) const - { - if (params.empty()) - { - throw chaiscript::exception::arity_error(static_cast(params.size()), 1); - } - - const Const_Proxy_Function &f = this->boxed_cast(params[0]); - const Type_Conversions_State convs(m_conversions, m_conversions.conversion_saves()); - - return const_var(f->call_match(std::vector(params.begin() + 1, params.end()), convs)); + try { + const dispatch::Dynamic_Object &d = boxed_cast(r); + return d.get_type_name() == user_typename; + } catch (const std::bad_cast &) { } - /// Dump all system info to stdout - void dump_system() const - { - std::cout << "Registered Types: \n"; - for (auto const &type: get_types()) - { - std::cout << type.first << ": " << type.second.bare_name() << '\n'; - } + return false; + } - std::cout << '\n'; + std::string type_name(const Boxed_Value &obj) const { return get_type_name(obj.get_type_info()); } - std::cout << "Functions: \n"; - for (auto const &func: get_functions()) - { - dump_function(func); - } - std::cout << '\n'; + State get_state() const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + return m_state; + } + + void set_state(const State &t_state) { + chaiscript::detail::threading::unique_lock l(m_mutex); + + m_state = t_state; + } + + static void save_function_params(Stack_Holder &t_s, std::vector &&t_params) { + for (auto &¶m : t_params) { + t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(param)); + } + } + + static void save_function_params(Stack_Holder &t_s, const Function_Params &t_params) { + t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end()); + } + + void save_function_params(std::vector &&t_params) { save_function_params(*m_stack_holder, std::move(t_params)); } + + void save_function_params(const Function_Params &t_params) { save_function_params(*m_stack_holder, t_params); } + + void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { + if (t_s.call_depth == 0) { + m_conversions.enable_conversion_saves(t_saves, true); } - /// return true if the Boxed_Value matches the registered type by name - bool is_type(const Boxed_Value &r, const std::string &user_typename) const - { - try { - if (get_type(user_typename).bare_equal(r.get_type_info())) - { - return true; - } - } catch (const std::range_error &) { - } + ++t_s.call_depth; - try { - const dispatch::Dynamic_Object &d = boxed_cast(r); - return d.get_type_name() == user_typename; - } catch (const std::bad_cast &) { - } + save_function_params(m_conversions.take_saves(t_saves)); + } + void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) { + --t_s.call_depth; + + assert(t_s.call_depth >= 0); + + if (t_s.call_depth == 0) { + t_s.call_params.back().clear(); + m_conversions.enable_conversion_saves(t_saves, false); + } + } + + void new_function_call() { new_function_call(*m_stack_holder, m_conversions.conversion_saves()); } + + void pop_function_call() { pop_function_call(*m_stack_holder, m_conversions.conversion_saves()); } + + Stack_Holder &get_stack_holder() noexcept { return *m_stack_holder; } + + /// Returns the current stack + /// make const/non const versions + const StackData &get_stack_data() const noexcept { return m_stack_holder->stacks.back(); } + + static StackData &get_stack_data(Stack_Holder &t_holder) noexcept { return t_holder.stacks.back(); } + + StackData &get_stack_data() noexcept { return m_stack_holder->stacks.back(); } + + parser::ChaiScript_Parser_Base &get_parser() noexcept { return m_parser.get(); } + + private: + const decltype(State::m_boxed_functions) &get_boxed_functions_int() const noexcept { return m_state.m_boxed_functions; } + + decltype(State::m_boxed_functions) &get_boxed_functions_int() noexcept { return m_state.m_boxed_functions; } + + const decltype(State::m_function_objects) &get_function_objects_int() const noexcept { return m_state.m_function_objects; } + + decltype(State::m_function_objects) &get_function_objects_int() noexcept { return m_state.m_function_objects; } + + const decltype(State::m_functions) &get_functions_int() const noexcept { return m_state.m_functions; } + + decltype(State::m_functions) &get_functions_int() noexcept { return m_state.m_functions; } + + static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs) noexcept { + auto dynamic_lhs(std::dynamic_pointer_cast(lhs)); + auto dynamic_rhs(std::dynamic_pointer_cast(rhs)); + + if (dynamic_lhs && dynamic_rhs) { + if (dynamic_lhs->get_guard()) { + return dynamic_rhs->get_guard() ? false : true; + } else { + return false; + } + } + + if (dynamic_lhs && !dynamic_rhs) { return false; } - std::string type_name(const Boxed_Value &obj) const - { - return get_type_name(obj.get_type_info()); + if (!dynamic_lhs && dynamic_rhs) { + return true; } - State get_state() const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + const auto &lhsparamtypes = lhs->get_param_types(); + const auto &rhsparamtypes = rhs->get_param_types(); - return m_state; - } + const auto lhssize = lhsparamtypes.size(); + const auto rhssize = rhsparamtypes.size(); - void set_state(const State &t_state) - { - chaiscript::detail::threading::unique_lock l(m_mutex); + const auto boxed_type = user_type(); + const auto boxed_pod_type = user_type(); - m_state = t_state; - } + for (size_t i = 1; i < lhssize && i < rhssize; ++i) { + const Type_Info < = lhsparamtypes[i]; + const Type_Info &rt = rhsparamtypes[i]; - static void save_function_params(Stack_Holder &t_s, std::initializer_list t_params) - { - t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params); - } - - static void save_function_params(Stack_Holder &t_s, std::vector &&t_params) - { - for (auto &¶m : t_params) - { - t_s.call_params.back().insert(t_s.call_params.back().begin(), std::move(param)); - } - } - - static void save_function_params(Stack_Holder &t_s, const std::vector &t_params) - { - t_s.call_params.back().insert(t_s.call_params.back().begin(), t_params.begin(), t_params.end()); - } - - void save_function_params(std::initializer_list t_params) - { - save_function_params(*m_stack_holder, t_params); - } - - void save_function_params(std::vector &&t_params) - { - save_function_params(*m_stack_holder, std::move(t_params)); - } - - void save_function_params(const std::vector &t_params) - { - save_function_params(*m_stack_holder, t_params); - } - - void new_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) - { - if (t_s.call_depth == 0) - { - m_conversions.enable_conversion_saves(t_saves, true); + if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) { + continue; // The first two types are essentially the same, next iteration } - ++t_s.call_depth; - - save_function_params(m_conversions.take_saves(t_saves)); - } - - void pop_function_call(Stack_Holder &t_s, Type_Conversions::Conversion_Saves &t_saves) - { - --t_s.call_depth; - - assert(t_s.call_depth >= 0); - - if (t_s.call_depth == 0) - { - t_s.call_params.back().clear(); - m_conversions.enable_conversion_saves(t_saves, false); - } - } - - void new_function_call() - { - new_function_call(*m_stack_holder, m_conversions.conversion_saves()); - } - - void pop_function_call() - { - pop_function_call(*m_stack_holder, m_conversions.conversion_saves()); - } - - Stack_Holder &get_stack_holder() - { - return *m_stack_holder; - } - - /// Returns the current stack - /// make const/non const versions - const StackData &get_stack_data() const - { - return m_stack_holder->stacks.back(); - } - - static StackData &get_stack_data(Stack_Holder &t_holder) - { - return t_holder.stacks.back(); - } - - StackData &get_stack_data() - { - return m_stack_holder->stacks.back(); - } - - parser::ChaiScript_Parser_Base &get_parser() - { - return m_parser.get(); - } - - private: - - const std::vector> &get_boxed_functions_int() const - { - return m_state.m_boxed_functions; - } - - std::vector> &get_boxed_functions_int() - { - return m_state.m_boxed_functions; - } - - const std::vector> &get_function_objects_int() const - { - return m_state.m_function_objects; - } - - std::vector> &get_function_objects_int() - { - return m_state.m_function_objects; - } - - const std::vector>>> &get_functions_int() const - { - return m_state.m_functions; - } - - std::vector>>> &get_functions_int() - { - return m_state.m_functions; - } - - static bool function_less_than(const Proxy_Function &lhs, const Proxy_Function &rhs) - { - - auto dynamic_lhs(std::dynamic_pointer_cast(lhs)); - auto dynamic_rhs(std::dynamic_pointer_cast(rhs)); - - if (dynamic_lhs && dynamic_rhs) - { - if (dynamic_lhs->get_guard()) - { - return dynamic_rhs->get_guard() ? false : true; - } else { - return false; - } - } - - if (dynamic_lhs && !dynamic_rhs) - { + // const is after non-const for the same type + if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) { return false; } - if (!dynamic_lhs && dynamic_rhs) - { + if (lt.bare_equal(rt) && !lt.is_const()) { return true; } - const auto &lhsparamtypes = lhs->get_param_types(); - const auto &rhsparamtypes = rhs->get_param_types(); - - const auto lhssize = lhsparamtypes.size(); - const auto rhssize = rhsparamtypes.size(); - - static const auto boxed_type = user_type(); - static const auto boxed_pod_type = user_type(); - - for (size_t i = 1; i < lhssize && i < rhssize; ++i) - { - const Type_Info < = lhsparamtypes[i]; - const Type_Info &rt = rhsparamtypes[i]; - - if (lt.bare_equal(rt) && lt.is_const() == rt.is_const()) - { - continue; // The first two types are essentially the same, next iteration - } - - // const is after non-const for the same type - if (lt.bare_equal(rt) && lt.is_const() && !rt.is_const()) - { - return false; - } - - if (lt.bare_equal(rt) && !lt.is_const()) - { - return true; - } - - // boxed_values are sorted last - if (lt.bare_equal(boxed_type)) - { - return false; - } - - if (rt.bare_equal(boxed_type)) - { - return true; - } - - if (lt.bare_equal(boxed_pod_type)) - { - return false; - } - - if (rt.bare_equal(boxed_pod_type)) - { - return true; - } - - // otherwise, we want to sort by typeid - return lt < rt; + // boxed_values are sorted last + if (lt.bare_equal(boxed_type)) { + return false; } - return false; + if (rt.bare_equal(boxed_type)) { + return true; + } + + if (lt.bare_equal(boxed_pod_type)) { + return false; + } + + if (rt.bare_equal(boxed_pod_type)) { + return true; + } + + // otherwise, we want to sort by typeid + return lt < rt; } + return false; + } + /// Implementation detail for adding a function. + /// \throws exception::name_conflict_error if there's a function matching the given one being added + void add_function(const Proxy_Function &t_f, const std::string &t_name) { + chaiscript::detail::threading::unique_lock l(m_mutex); - template - static void add_keyed_value(Container &t_c, const Key &t_key, Value &&t_value) - { - auto itr = find_keyed_value(t_c, t_key); - - if (itr == t_c.end()) { - t_c.reserve(t_c.size() + 1); // tightly control growth of memory usage here - t_c.emplace_back(t_key, std::forward(t_value)); - } else { - typedef typename Container::value_type value_type; - *itr = value_type(t_key, std::forward(t_value)); - } - } - - template - static typename Container::iterator find_keyed_value(Container &t_c, const Key &t_key) - { - return std::find_if(t_c.begin(), t_c.end(), - [&t_key](const typename Container::value_type &o) { - return o.first == t_key; - }); - } - - template - static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key) - { - return std::find_if(t_c.begin(), t_c.end(), - [&t_key](const typename Container::value_type &o) { - return o.first == t_key; - }); - } - - template - static typename Container::const_iterator find_keyed_value(const Container &t_c, const Key &t_key, const size_t t_hint) - { - if (t_c.size() > t_hint && t_c[t_hint].first == t_key) { - return std::next(t_c.begin(), static_cast::difference_type>(t_hint)); - } else { - return find_keyed_value(t_c, t_key); - } - } - - - /// Implementation detail for adding a function. - /// \throws exception::name_conflict_error if there's a function matching the given one being added - void add_function(const Proxy_Function &t_f, const std::string &t_name) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - + Proxy_Function new_func = [&]() -> Proxy_Function { auto &funcs = get_functions_int(); + auto itr = funcs.find(t_name); - auto itr = find_keyed_value(funcs, t_name); - - Proxy_Function new_func = - [&]() -> Proxy_Function { - if (itr != funcs.end()) - { - auto vec = *itr->second; - for (const auto &func : vec) - { - if ((*t_f) == *(func)) - { - throw chaiscript::exception::name_conflict_error(t_name); - } - } - - vec.reserve(vec.size() + 1); // tightly control vec growth - vec.push_back(t_f); - std::stable_sort(vec.begin(), vec.end(), &function_less_than); - itr->second = std::make_shared>(vec); - return std::make_shared(std::move(vec)); - } else if (t_f->has_arithmetic_param()) { - // if the function is the only function but it also contains - // arithmetic operators, we must wrap it in a dispatch function - // to allow for automatic arithmetic type conversions - std::vector vec({t_f}); - funcs.emplace_back(t_name, std::make_shared>(vec)); - return std::make_shared(std::move(vec)); - } else { - funcs.emplace_back(t_name, std::make_shared>(std::initializer_list({t_f}))); - return t_f; + if (itr != funcs.end()) { + auto vec = *itr->second; + for (const auto &func : vec) { + if ((*t_f) == *(func)) { + throw chaiscript::exception::name_conflict_error(t_name); } - }(); + } - add_keyed_value(get_boxed_functions_int(), t_name, const_var(new_func)); - add_keyed_value(get_function_objects_int(), t_name, std::move(new_func)); - } + vec.reserve(vec.size() + 1); // tightly control vec growth + vec.push_back(t_f); + std::stable_sort(vec.begin(), vec.end(), &function_less_than); + itr->second = std::make_shared>(vec); + return std::make_shared(std::move(vec)); + } else if (t_f->has_arithmetic_param()) { + // if the function is the only function but it also contains + // arithmetic operators, we must wrap it in a dispatch function + // to allow for automatic arithmetic type conversions + std::vector vec; + vec.push_back(t_f); + funcs.insert(std::pair{t_name, std::make_shared>(vec)}); + return std::make_shared(std::move(vec)); + } else { + auto vec = std::make_shared>(); + vec->push_back(t_f); + funcs.insert(std::pair{t_name, vec}); + return t_f; + } + }(); - mutable chaiscript::detail::threading::shared_mutex m_mutex; + get_boxed_functions_int().insert_or_assign(t_name, const_var(new_func)); + get_function_objects_int().insert_or_assign(t_name, std::move(new_func)); + } + mutable chaiscript::detail::threading::shared_mutex m_mutex; - Type_Conversions m_conversions; - chaiscript::detail::threading::Thread_Storage m_stack_holder; - std::reference_wrapper m_parser; + Type_Conversions m_conversions; + chaiscript::detail::threading::Thread_Storage m_stack_holder; + std::reference_wrapper m_parser; - mutable std::atomic_uint_fast32_t m_method_missing_loc = {0}; + mutable std::atomic_uint_fast32_t m_method_missing_loc = {0}; - State m_state; + State m_state; }; - class Dispatch_State - { - public: - explicit Dispatch_State(Dispatch_Engine &t_engine) - : m_engine(t_engine), - m_stack_holder(t_engine.get_stack_holder()), - m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves()) - { - } + class Dispatch_State { + public: + explicit Dispatch_State(Dispatch_Engine &t_engine) + : m_engine(t_engine) + , m_stack_holder(t_engine.get_stack_holder()) + , m_conversions(t_engine.conversions(), t_engine.conversions().conversion_saves()) { + } - Dispatch_Engine *operator->() const { - return &m_engine.get(); - } + Dispatch_Engine *operator->() const noexcept { return &m_engine.get(); } - Dispatch_Engine &operator*() const { - return m_engine.get(); - } + Dispatch_Engine &operator*() const noexcept { return m_engine.get(); } - Stack_Holder &stack_holder() const { - return m_stack_holder.get(); - } + Stack_Holder &stack_holder() const noexcept { return m_stack_holder.get(); } - const Type_Conversions_State &conversions() const { - return m_conversions; - } + const Type_Conversions_State &conversions() const noexcept { return m_conversions; } - Type_Conversions::Conversion_Saves &conversion_saves() const { - return m_conversions.saves(); - } + Type_Conversions::Conversion_Saves &conversion_saves() const noexcept { return m_conversions.saves(); } - Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const { - return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get()); - } + Boxed_Value &add_get_object(const std::string &t_name, Boxed_Value obj) const { + return m_engine.get().add_get_object(t_name, std::move(obj), m_stack_holder.get()); + } - void add_object(const std::string &t_name, Boxed_Value obj) const { - return m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); - } + void add_object(const std::string &t_name, Boxed_Value obj) const { + m_engine.get().add_object(t_name, std::move(obj), m_stack_holder.get()); + } - Boxed_Value get_object(const std::string &t_name, std::atomic_uint_fast32_t &t_loc) const { - return m_engine.get().get_object(t_name, t_loc, m_stack_holder.get()); - } + Boxed_Value get_object(std::string_view t_name, std::atomic_uint_fast32_t &t_loc) const { + return m_engine.get().get_object(t_name, t_loc, m_stack_holder.get()); + } - private: - std::reference_wrapper m_engine; - std::reference_wrapper m_stack_holder; - Type_Conversions_State m_conversions; + private: + std::reference_wrapper m_engine; + std::reference_wrapper m_stack_holder; + Type_Conversions_State m_conversions; }; - } -} + } // namespace detail +} // namespace chaiscript #endif - - diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index b5afaf15..b242a207 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_DYNAMIC_OBJECT_HPP_ #define CHAISCRIPT_DYNAMIC_OBJECT_HPP_ @@ -18,21 +17,17 @@ #include "boxed_value.hpp" namespace chaiscript { -class Type_Conversions; -namespace dispatch { -class Proxy_Function_Base; -} // namespace dispatch -} // namespace chaiscript + class Type_Conversions; + namespace dispatch { + class Proxy_Function_Base; + } // namespace dispatch +} // namespace chaiscript -namespace chaiscript -{ - namespace dispatch - { +namespace chaiscript { + namespace dispatch { struct option_explicit_set : std::runtime_error { explicit option_explicit_set(const std::string &t_param_name) - : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") - { - + : std::runtime_error("option explicit set and parameter '" + t_param_name + "' does not exist") { } option_explicit_set(const option_explicit_set &) = default; @@ -40,92 +35,64 @@ namespace chaiscript ~option_explicit_set() noexcept override = default; }; - class Dynamic_Object - { - public: - explicit Dynamic_Object(std::string t_type_name) - : m_type_name(std::move(t_type_name)), m_option_explicit(false) - { + class Dynamic_Object { + public: + explicit Dynamic_Object(std::string t_type_name) + : m_type_name(std::move(t_type_name)) + , m_option_explicit(false) { + } + + Dynamic_Object() = default; + + bool is_explicit() const noexcept { return m_option_explicit; } + + void set_explicit(const bool t_explicit) noexcept { m_option_explicit = t_explicit; } + + const std::string &get_type_name() const noexcept { return m_type_name; } + + const Boxed_Value &operator[](const std::string &t_attr_name) const { return get_attr(t_attr_name); } + + Boxed_Value &operator[](const std::string &t_attr_name) { return get_attr(t_attr_name); } + + const Boxed_Value &get_attr(const std::string &t_attr_name) const { + auto a = m_attrs.find(t_attr_name); + + if (a != m_attrs.end()) { + return a->second; + } else { + throw std::range_error("Attr not found '" + t_attr_name + "' and cannot be added to const obj"); + } + } + + bool has_attr(const std::string &t_attr_name) const { return m_attrs.find(t_attr_name) != m_attrs.end(); } + + Boxed_Value &get_attr(const std::string &t_attr_name) { return m_attrs[t_attr_name]; } + + Boxed_Value &method_missing(const std::string &t_method_name) { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); } - Dynamic_Object() = default; + return get_attr(t_method_name); + } - bool is_explicit() const - { - return m_option_explicit; + const Boxed_Value &method_missing(const std::string &t_method_name) const { + if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { + throw option_explicit_set(t_method_name); } - void set_explicit(const bool t_explicit) - { - m_option_explicit = t_explicit; - } + return get_attr(t_method_name); + } - std::string get_type_name() const - { - return m_type_name; - } + std::map get_attrs() const { return m_attrs; } - const Boxed_Value &operator[](const std::string &t_attr_name) const - { - return get_attr(t_attr_name); - } + private: + const std::string m_type_name = ""; + bool m_option_explicit = false; - Boxed_Value &operator[](const std::string &t_attr_name) - { - return get_attr(t_attr_name); - } - - const Boxed_Value &get_attr(const std::string &t_attr_name) const - { - auto a = m_attrs.find(t_attr_name); - - if (a != m_attrs.end()) { - return a->second; - } else { - throw std::range_error("Attr not found '" + t_attr_name + "' and cannot be added to const obj"); - } - } - - bool has_attr(const std::string &t_attr_name) const { - return m_attrs.find(t_attr_name) != m_attrs.end(); - } - - Boxed_Value &get_attr(const std::string &t_attr_name) - { - return m_attrs[t_attr_name]; - } - - Boxed_Value &method_missing(const std::string &t_method_name) - { - if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { - throw option_explicit_set(t_method_name); - } - - return get_attr(t_method_name); - } - - const Boxed_Value &method_missing(const std::string &t_method_name) const - { - if (m_option_explicit && m_attrs.find(t_method_name) == m_attrs.end()) { - throw option_explicit_set(t_method_name); - } - - return get_attr(t_method_name); - } - - std::map get_attrs() const - { - return m_attrs; - } - - private: - const std::string m_type_name = ""; - bool m_option_explicit = false; - - std::map m_attrs; + std::map m_attrs; }; - } -} + } // namespace dispatch +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp index df90ab66..07477c1e 100644 --- a/include/chaiscript/dispatchkit/dynamic_object_detail.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object_detail.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_DYNAMIC_OBJECT_DETAIL_HPP_ @@ -19,219 +19,185 @@ #include "boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" +#include "dynamic_object.hpp" #include "proxy_functions.hpp" #include "type_info.hpp" -#include "dynamic_object.hpp" namespace chaiscript { -class Type_Conversions; -namespace dispatch { -class Proxy_Function_Base; -} // namespace dispatch -} // namespace chaiscript + class Type_Conversions; + namespace dispatch { + class Proxy_Function_Base; + } // namespace dispatch +} // namespace chaiscript -namespace chaiscript -{ - namespace dispatch - { - namespace detail - { +namespace chaiscript { + namespace dispatch { + namespace detail { /// A Proxy_Function implementation designed for calling a function /// that is automatically guarded based on the first param based on the /// param's type name - class Dynamic_Object_Function final : public Proxy_Function_Base - { - public: - Dynamic_Object_Function( - std::string t_type_name, - const Proxy_Function &t_func, - bool t_is_attribute = false) - : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_doti(user_type()), - m_is_attribute(t_is_attribute) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + class Dynamic_Object_Function final : public Proxy_Function_Base { + public: + Dynamic_Object_Function(std::string t_type_name, const Proxy_Function &t_func, bool t_is_attribute = false) + : Proxy_Function_Base(t_func->get_param_types(), t_func->get_arity()) + , m_type_name(std::move(t_type_name)) + , m_func(t_func) + , m_doti(user_type()) + , m_is_attribute(t_is_attribute) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + Dynamic_Object_Function(std::string t_type_name, const Proxy_Function &t_func, const Type_Info &t_ti, bool t_is_attribute = false) + : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()) + , m_type_name(std::move(t_type_name)) + , m_func(t_func) + , m_ti(t_ti.is_undef() ? nullptr : new Type_Info(t_ti)) + , m_doti(user_type()) + , m_is_attribute(t_is_attribute) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; + Dynamic_Object_Function(Dynamic_Object_Function &) = delete; + + bool operator==(const Proxy_Function_Base &f) const noexcept override { + if (const auto *df = dynamic_cast(&f)) { + return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); + } else { + return false; } + } - Dynamic_Object_Function( - std::string t_type_name, - const Proxy_Function &t_func, - const Type_Info &t_ti, - bool t_is_attribute = false) - : Proxy_Function_Base(build_param_types(t_func->get_param_types(), t_ti), t_func->get_arity()), - m_type_name(std::move(t_type_name)), m_func(t_func), m_ti(t_ti.is_undef()?nullptr:new Type_Info(t_ti)), m_doti(user_type()), - m_is_attribute(t_is_attribute) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + bool is_attribute_function() const noexcept override { return m_is_attribute; } + + bool call_match(const chaiscript::Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { + if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) { + return m_func->call_match(vals, t_conversions); + } else { + return false; } + } + std::vector get_contained_functions() const override { return {m_func}; } - Dynamic_Object_Function &operator=(const Dynamic_Object_Function) = delete; - Dynamic_Object_Function(Dynamic_Object_Function &) = delete; + protected: + Boxed_Value do_call(const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) { + return (*m_func)(params, t_conversions); + } else { + throw exception::guard_error(); + } + } - bool operator==(const Proxy_Function_Base &f) const override - { - if (const auto *df = dynamic_cast(&f)) - { - return df->m_type_name == m_type_name && (*df->m_func) == (*m_func); + bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept override { + return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); + } + + private: + static std::vector build_param_types(const std::vector &t_inner_types, const Type_Info &t_objectti) { + std::vector types(t_inner_types); + + assert(types.size() > 1); + // assert(types[1].bare_equal(user_type())); + types[1] = t_objectti; + return types; + } + + bool dynamic_object_typename_match(const Boxed_Value &bv, + const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept { + if (bv.get_type_info().bare_equal(m_doti)) { + try { + const Dynamic_Object &d = boxed_cast(bv, &t_conversions); + return name == "Dynamic_Object" || d.get_type_name() == name; + } catch (const std::bad_cast &) { + return false; + } + } else { + if (ti) { + return bv.get_type_info().bare_equal(*ti); } else { return false; } } + } - bool is_attribute_function() const override { return m_is_attribute; } - - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - if (dynamic_object_typename_match(vals, m_type_name, m_ti, t_conversions)) - { - return m_func->call_match(vals, t_conversions); - } else { - return false; - } + bool dynamic_object_typename_match(const chaiscript::Function_Params &bvs, + const std::string &name, + const std::unique_ptr &ti, + const Type_Conversions_State &t_conversions) const noexcept { + if (!bvs.empty()) { + return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); + } else { + return false; } + } - std::vector get_contained_functions() const override - { - return {m_func}; - } - - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - if (dynamic_object_typename_match(params, m_type_name, m_ti, t_conversions)) - { - return (*m_func)(params, t_conversions); - } else { - throw exception::guard_error(); - } - } - - bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const override - { - return dynamic_object_typename_match(bv, m_type_name, m_ti, t_conversions); - } - - private: - static std::vector build_param_types( - const std::vector &t_inner_types, const Type_Info& t_objectti) - { - std::vector types(t_inner_types); - - assert(types.size() > 1); - //assert(types[1].bare_equal(user_type())); - types[1] = t_objectti; - return types; - } - - bool dynamic_object_typename_match(const Boxed_Value &bv, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const - { - if (bv.get_type_info().bare_equal(m_doti)) - { - try { - const Dynamic_Object &d = boxed_cast(bv, &t_conversions); - return name == "Dynamic_Object" || d.get_type_name() == name; - } catch (const std::bad_cast &) { - return false; - } - } else { - if (ti) - { - return bv.get_type_info().bare_equal(*ti); - } else { - return false; - } - } - - } - - bool dynamic_object_typename_match(const std::vector &bvs, const std::string &name, - const std::unique_ptr &ti, const Type_Conversions_State &t_conversions) const - { - if (!bvs.empty()) - { - return dynamic_object_typename_match(bvs[0], name, ti, t_conversions); - } else { - return false; - } - } - - std::string m_type_name; - Proxy_Function m_func; - std::unique_ptr m_ti; - const Type_Info m_doti; - const bool m_is_attribute; + std::string m_type_name; + Proxy_Function m_func; + std::unique_ptr m_ti; + const Type_Info m_doti; + const bool m_is_attribute; }; - /** - * A Proxy_Function implementation designed for creating a new - * Dynamic_Object - * that is automatically guarded based on the first param based on the - * param's type name - */ - class Dynamic_Object_Constructor final : public Proxy_Function_Base - { - public: - Dynamic_Object_Constructor( - std::string t_type_name, - const Proxy_Function &t_func) - : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1), - m_type_name(std::move(t_type_name)), m_func(t_func) - { - assert( (t_func->get_arity() > 0 || t_func->get_arity() < 0) - && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + * A Proxy_Function implementation designed for creating a new + * Dynamic_Object + * that is automatically guarded based on the first param based on the + * param's type name + */ + class Dynamic_Object_Constructor final : public Proxy_Function_Base { + public: + Dynamic_Object_Constructor(std::string t_type_name, const Proxy_Function &t_func) + : Proxy_Function_Base(build_type_list(t_func->get_param_types()), t_func->get_arity() - 1) + , m_type_name(std::move(t_type_name)) + , m_func(t_func) { + assert((t_func->get_arity() > 0 || t_func->get_arity() < 0) + && "Programming error, Dynamic_Object_Function must have at least one parameter (this)"); + } + + static std::vector build_type_list(const std::vector &tl) { + auto begin = tl.begin(); + auto end = tl.end(); + + if (begin != end) { + ++begin; } - static std::vector build_type_list(const std::vector &tl) - { - auto begin = tl.begin(); - auto end = tl.end(); + return std::vector(begin, end); + } - if (begin != end) - { - ++begin; - } + bool operator==(const Proxy_Function_Base &f) const noexcept override { + const Dynamic_Object_Constructor *dc = dynamic_cast(&f); + return (dc != nullptr) && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); + } - return std::vector(begin, end); - } + bool call_match(const chaiscript::Function_Params &vals, const Type_Conversions_State &t_conversions) const override { + std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; + new_vals.insert(new_vals.end(), vals.begin(), vals.end()); - bool operator==(const Proxy_Function_Base &f) const override - { - const Dynamic_Object_Constructor *dc = dynamic_cast(&f); - return (dc != nullptr) && dc->m_type_name == m_type_name && (*dc->m_func) == (*m_func); - } + return m_func->call_match(chaiscript::Function_Params{new_vals}, t_conversions); + } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - std::vector new_vals{Boxed_Value(Dynamic_Object(m_type_name))}; - new_vals.insert(new_vals.end(), vals.begin(), vals.end()); + protected: + Boxed_Value do_call(const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); + std::vector new_params{bv}; + new_params.insert(new_params.end(), params.begin(), params.end()); - return m_func->call_match(new_vals, t_conversions); - } + (*m_func)(chaiscript::Function_Params{new_params}, t_conversions); - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - auto bv = Boxed_Value(Dynamic_Object(m_type_name), true); - std::vector new_params{bv}; - new_params.insert(new_params.end(), params.begin(), params.end()); - - (*m_func)(new_params, t_conversions); - - return bv; - } - - private: - const std::string m_type_name; - const Proxy_Function m_func; + return bv; + } + private: + const std::string m_type_name; + const Proxy_Function m_func; }; - } - } -} + } // namespace detail + } // namespace dispatch +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/exception_specification.hpp b/include/chaiscript/dispatchkit/exception_specification.hpp index 79607fe2..3ee32a83 100644 --- a/include/chaiscript/dispatchkit/exception_specification.hpp +++ b/include/chaiscript/dispatchkit/exception_specification.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ #define CHAISCRIPT_EXCEPTION_SPECIFICATION_HPP_ @@ -17,39 +16,28 @@ #include "boxed_cast.hpp" namespace chaiscript { -class Boxed_Value; -namespace exception { -class bad_boxed_cast; -} // namespace exception -} // namespace chaiscript - -namespace chaiscript -{ - namespace detail - { - struct Exception_Handler_Base - { + namespace detail { + struct Exception_Handler_Base { virtual void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) = 0; virtual ~Exception_Handler_Base() = default; - protected: - template - static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) - { - try { T t = t_engine.boxed_cast(bv); throw t; } catch (const chaiscript::exception::bad_boxed_cast &) {} + protected: + template + static void throw_type(const Boxed_Value &bv, const Dispatch_Engine &t_engine) { + try { + T t = t_engine.boxed_cast(bv); + throw t; + } catch (const chaiscript::exception::bad_boxed_cast &) { } + } }; - template - struct Exception_Handler_Impl : Exception_Handler_Base - { - void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) override - { - (void)std::initializer_list{(throw_type(bv, t_engine), 0)...}; - } - }; - } + template + struct Exception_Handler_Impl : Exception_Handler_Base { + void handle(const Boxed_Value &bv, const Dispatch_Engine &t_engine) override { (throw_type(bv, t_engine), ...); } + }; + } // namespace detail /// \brief Used in the automatic unboxing of exceptions thrown during script evaluation /// @@ -61,7 +49,8 @@ namespace chaiscript /// chaiscript::ChaiScript chai; /// /// try { - /// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); + /// chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); /// } catch (const double e) { /// } catch (int) { /// } catch (float) { @@ -86,7 +75,7 @@ namespace chaiscript /// /// Similarly, if you are using the ChaiScript::eval form that unboxes the return value, then chaiscript::exception::bad_boxed_cast /// should be handled as well. - /// + /// /// \code /// try { /// chai.eval("1.0", chaiscript::exception_specification()); @@ -101,17 +90,14 @@ namespace chaiscript /// /// \sa chaiscript::exception_specification for creation of chaiscript::Exception_Handler objects /// \sa \ref exceptions - typedef std::shared_ptr Exception_Handler; + using Exception_Handler = std::shared_ptr; /// \brief creates a chaiscript::Exception_Handler which handles one type of exception unboxing /// \sa \ref exceptions - template - Exception_Handler exception_specification() - { + template + Exception_Handler exception_specification() { return std::make_shared>(); } -} - +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/function_call.hpp b/include/chaiscript/dispatchkit/function_call.hpp index 1980f35c..f7589b33 100644 --- a/include/chaiscript/dispatchkit/function_call.hpp +++ b/include/chaiscript/dispatchkit/function_call.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_FUNCTION_CALL_HPP_ #define CHAISCRIPT_FUNCTION_CALL_HPP_ @@ -18,20 +17,25 @@ #include "boxed_cast.hpp" #include "function_call_detail.hpp" #include "proxy_functions.hpp" -#include "callable_traits.hpp" namespace chaiscript { -class Boxed_Value; -class Type_Conversions_State; -namespace detail { -template struct Cast_Helper; -} // namespace detail -} // namespace chaiscript + class Boxed_Value; + class Type_Conversions_State; + namespace detail { + template + struct Cast_Helper; + } // namespace detail +} // namespace chaiscript + +namespace chaiscript { + namespace dispatch { + namespace detail { + template + constexpr auto arity(Ret (*)(Param...)) noexcept { + return sizeof...(Param); + } + } // namespace detail -namespace chaiscript -{ - namespace dispatch - { /// Build a function caller that knows how to dispatch on a set of functions /// example: /// std::function f = @@ -39,21 +43,19 @@ namespace chaiscript /// \returns A std::function object for dispatching /// \param[in] funcs the set of functions to dispatch on. template - std::function functor(const std::vector &funcs, const Type_Conversions_State *t_conversions) - { - const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), - [](const Const_Proxy_Function &f) { - return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity::arity; - }); + std::function functor(const std::vector &funcs, const Type_Conversions_State *t_conversions) { + const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(), [](const Const_Proxy_Function &f) { + return f->get_arity() == -1 || size_t(f->get_arity()) == detail::arity(static_cast(nullptr)); + }); - if (!has_arity_match) { - throw exception::bad_boxed_cast(user_type(), typeid(std::function)); - } - - FunctionType *p=nullptr; - return detail::build_function_caller_helper(p, funcs, t_conversions); + if (!has_arity_match) { + throw exception::bad_boxed_cast(user_type(), typeid(std::function)); } + FunctionType *p = nullptr; + return detail::build_function_caller_helper(p, funcs, t_conversions); + } + /// Build a function caller for a particular Proxy_Function object /// useful in the case that a function is being pass out from scripting back /// into code @@ -66,67 +68,55 @@ namespace chaiscript /// \returns A std::function object for dispatching /// \param[in] func A function to execute. template - std::function functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions) - { - return functor(std::vector({std::move(func)}), t_conversions); - } + std::function functor(Const_Proxy_Function func, const Type_Conversions_State *t_conversions) { + return functor(std::vector({std::move(func)}), t_conversions); + } /// Helper for automatically unboxing a Boxed_Value that contains a function object /// and creating a typesafe C++ function caller from it. template - std::function functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions) - { - return functor(boxed_cast(bv, t_conversions), t_conversions); - } - } + std::function functor(const Boxed_Value &bv, const Type_Conversions_State *t_conversions) { + return functor(boxed_cast(bv, t_conversions), t_conversions); + } + } // namespace dispatch - namespace detail{ + namespace detail { /// Cast helper to handle automatic casting to const std::function & template - struct Cast_Helper &> - { - static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) - { - if (ob.get_type_info().bare_equal(user_type())) - { - return dispatch::functor(ob, t_conversions); - } else { - return Cast_Helper_Inner &>::cast(ob, t_conversions); - } + struct Cast_Helper &> { + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner &>::cast(ob, t_conversions); } - }; + } + }; /// Cast helper to handle automatic casting to std::function template - struct Cast_Helper > - { - static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) - { - if (ob.get_type_info().bare_equal(user_type())) - { - return dispatch::functor(ob, t_conversions); - } else { - return Cast_Helper_Inner >::cast(ob, t_conversions); - } + struct Cast_Helper> { + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner>::cast(ob, t_conversions); } - }; + } + }; /// Cast helper to handle automatic casting to const std::function template - struct Cast_Helper > - { - static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) - { - if (ob.get_type_info().bare_equal(user_type())) - { - return dispatch::functor(ob, t_conversions); - } else { - return Cast_Helper_Inner >::cast(ob, t_conversions); - } + struct Cast_Helper> { + static std::function cast(const Boxed_Value &ob, const Type_Conversions_State *t_conversions) { + if (ob.get_type_info().bare_equal(user_type())) { + return dispatch::functor(ob, t_conversions); + } else { + return Cast_Helper_Inner>::cast(ob, t_conversions); } - }; - } -} + } + }; + } // namespace detail +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index d6cb241f..f67a5a00 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ #define CHAISCRIPT_FUNCTION_CALL_DETAIL_HPP_ @@ -20,152 +19,79 @@ #include "boxed_cast.hpp" #include "boxed_number.hpp" #include "boxed_value.hpp" -#include "type_conversions.hpp" #include "proxy_functions.hpp" +#include "type_conversions.hpp" -namespace chaiscript -{ - namespace dispatch - { - namespace detail - { - /// Internal helper class for handling the return - /// value of a build_function_caller - template - struct Function_Caller_Ret - { - static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - return boxed_cast(dispatch::dispatch(t_funcs, params, *t_conversions), t_conversions); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - return boxed_cast(dispatch::dispatch(t_funcs, params, state), t_conversions); - } - } - }; - - /** - * Specialization for arithmetic return types - */ - template - struct Function_Caller_Ret - { - static Ret call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - return Boxed_Number(dispatch::dispatch(t_funcs, params, *t_conversions)).get_as(); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - return Boxed_Number(dispatch::dispatch(t_funcs, params, state)).get_as(); - } - } - }; - - - /** - * Specialization for void return types - */ - template<> - struct Function_Caller_Ret - { - static void call(const std::vector &t_funcs, - const std::vector ¶ms, const Type_Conversions_State *t_conversions) - { - if (t_conversions != nullptr) { - dispatch::dispatch(t_funcs, params, *t_conversions); - } else { - Type_Conversions conv; - Type_Conversions_State state(conv, conv.conversion_saves()); - dispatch::dispatch(t_funcs, params, state); - } - } - }; - - /** - * used internally for unwrapping a function call's types - */ - template - struct Build_Function_Caller_Helper - { - Build_Function_Caller_Helper(std::vector t_funcs, const Type_Conversions *t_conversions) - : m_funcs(std::move(t_funcs)), - m_conversions(t_conversions) - { - } - - template - Ret operator()(P&& ... param) - { - if (m_conversions) { - Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves()); - return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { - box

(std::forward

(param))... - }, &state - ); - } else { - return Function_Caller_Ret::value && !std::is_same::value>::call(m_funcs, { - box

(std::forward

(param))... - }, nullptr - ); - } - - } - - template - static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type - { - return Boxed_Value(std::ref(std::forward(q))); - } - - template - static auto box(Q&& q) -> typename std::enable_if::value&&!std::is_same::type>::type>::value, Boxed_Value>::type - { - return Boxed_Value(std::forward(q)); - } - - template - static Boxed_Value box(Boxed_Value bv) - { - return bv; - } - - - std::vector m_funcs; - const Type_Conversions *m_conversions; - }; - - - - /// \todo what happens if t_conversions is deleted out from under us?! - template - std::function build_function_caller_helper(Ret (Params...), const std::vector &funcs, const Type_Conversions_State *t_conversions) - { - /* - if (funcs.size() == 1) - { - std::shared_ptr> pfi = - std::dynamic_pointer_cast > - (funcs[0]); - - if (pfi) - { - return pfi->internal_function(); - } - // looks like this either wasn't a Proxy_Function_Impl or the types didn't match - // we cannot make any other guesses or assumptions really, so continuing - } -*/ - - return std::function(Build_Function_Caller_Helper(funcs, t_conversions?t_conversions->get():nullptr)); - } +namespace chaiscript::dispatch::detail { + /// used internally for unwrapping a function call's types + template + struct Build_Function_Caller_Helper { + Build_Function_Caller_Helper(std::vector t_funcs, const Type_Conversions *t_conversions) + : m_funcs(std::move(t_funcs)) + , m_conversions(t_conversions) { } + + Ret call(const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_state) { + if constexpr (std::is_arithmetic_v && !std::is_same_v>, bool>) { + return Boxed_Number(dispatch::dispatch(m_funcs, params, t_state)).get_as(); + } else if constexpr (std::is_same_v) { + dispatch::dispatch(m_funcs, params, t_state); + } else { + return boxed_cast(dispatch::dispatch(m_funcs, params, t_state), &t_state); + } + } + + template + Ret operator()(P &&...param) { + std::array params{box

(std::forward

(param))...}; + + if (m_conversions) { + Type_Conversions_State state(*m_conversions, m_conversions->conversion_saves()); + return call(chaiscript::Function_Params{params}, state); + } else { + Type_Conversions conv; + Type_Conversions_State state(conv, conv.conversion_saves()); + return call(chaiscript::Function_Params{params}, state); + } + } + + template + static Boxed_Value box(Q &&q) { + if constexpr (std::is_same_v>) { + return std::forward(q); + } else if constexpr (std::is_reference_v

) { + return Boxed_Value(std::ref(std::forward(q))); + } else { + return Boxed_Value(std::forward(q)); + } + } + + std::vector m_funcs; + const Type_Conversions *m_conversions; + }; + + /// \todo what happens if t_conversions is deleted out from under us?! + template + std::function + build_function_caller_helper(Ret(Params...), const std::vector &funcs, const Type_Conversions_State *t_conversions) { + /* + if (funcs.size() == 1) + { + std::shared_ptr> pfi = + std::dynamic_pointer_cast > + (funcs[0]); + + if (pfi) + { + return pfi->internal_function(); + } + // looks like this either wasn't a Proxy_Function_Impl or the types didn't match + // we cannot make any other guesses or assumptions really, so continuing + } + */ + + return std::function(Build_Function_Caller_Helper(funcs, t_conversions ? t_conversions->get() : nullptr)); } -} +} // namespace chaiscript::dispatch::detail #endif - diff --git a/include/chaiscript/dispatchkit/function_params.hpp b/include/chaiscript/dispatchkit/function_params.hpp new file mode 100644 index 00000000..9a77c5ee --- /dev/null +++ b/include/chaiscript/dispatchkit/function_params.hpp @@ -0,0 +1,67 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#ifndef CHAISCRIPT_FUNCTION_PARAMS_HPP +#define CHAISCRIPT_FUNCTION_PARAMS_HPP + +#include "boxed_value.hpp" + +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) { + } + +} // namespace chaiscript + +#endif diff --git a/include/chaiscript/dispatchkit/function_signature.hpp b/include/chaiscript/dispatchkit/function_signature.hpp new file mode 100644 index 00000000..174f8576 --- /dev/null +++ b/include/chaiscript/dispatchkit/function_signature.hpp @@ -0,0 +1,149 @@ +#ifndef CHAISCRIPT_FUNCTION_SIGNATURE_HPP +#define CHAISCRIPT_FUNCTION_SIGNATURE_HPP + +#include + +namespace chaiscript::dispatch::detail { + template + struct Function_Params { + }; + + template + struct Function_Signature { + using Param_Types = Params; + using Return_Type = Ret; + constexpr static const bool is_object = IsObject; + constexpr static const bool is_member_object = IsMemberObject; + constexpr static const bool is_noexcept = IsNoExcept; + template + constexpr Function_Signature(T &&) noexcept { + } + constexpr Function_Signature() noexcept = default; + }; + + // Free functions + + template + Function_Signature(Ret (*f)(Param...)) -> Function_Signature>; + + template + Function_Signature(Ret (*f)(Param...) noexcept) -> Function_Signature, true>; + + // no reference specifier + + template + Function_Signature(Ret (Class::*f)(Param...) volatile) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const) + -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...)) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) noexcept) -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const noexcept) -> Function_Signature, true, true>; + + // & reference specifier + + template + Function_Signature(Ret (Class::*f)(Param...) volatile &) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile &noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const &) + -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const &noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) &) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) &noexcept) -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const &) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const &noexcept) -> Function_Signature, true, true>; + + // && reference specifier + + template + Function_Signature(Ret (Class::*f)(Param...) volatile &&) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile &&noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const &&) + -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) volatile const &&noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) &&) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) &&noexcept) -> Function_Signature, true, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const &&) -> Function_Signature, false, true>; + + template + Function_Signature(Ret (Class::*f)(Param...) const &&noexcept) + -> Function_Signature, true, true>; + + template + Function_Signature(Ret Class::*f) -> Function_Signature, true, true, true>; + + // primary template handles types that have no nested ::type member: + template> + struct has_call_operator : std::false_type { + }; + + // specialization recognizes types that do have a nested ::type member: + template + struct has_call_operator> : std::true_type { + }; + + template + auto function_signature(const Func &f) { + if constexpr (has_call_operator::value) { + return Function_Signature::operator()})::Return_Type, + typename decltype(Function_Signature{&std::decay_t::operator()})::Param_Types, + decltype(Function_Signature{&std::decay_t::operator()})::is_noexcept, + false, + false, + true>{}; + } else { + return Function_Signature{f}; + } + } + +} // namespace chaiscript::dispatch::detail + +#endif diff --git a/include/chaiscript/dispatchkit/handle_return.hpp b/include/chaiscript/dispatchkit/handle_return.hpp index 8570c8d0..e0a3b132 100644 --- a/include/chaiscript/dispatchkit/handle_return.hpp +++ b/include/chaiscript/dispatchkit/handle_return.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_HANDLE_RETURN_HPP_ #define CHAISCRIPT_HANDLE_RETURN_HPP_ @@ -19,238 +18,178 @@ #include "boxed_value.hpp" namespace chaiscript { -class Boxed_Number; -} // namespace chaiscript + class Boxed_Number; +} // namespace chaiscript -namespace chaiscript -{ - namespace dispatch - { - template class Proxy_Function_Callable_Impl; - template class Assignable_Proxy_Function_Impl; +namespace chaiscript { + namespace dispatch { + template + class Proxy_Function_Callable_Impl; + template + class Assignable_Proxy_Function_Impl; - namespace detail - { + namespace detail { /// Used internally for handling a return value from a Proxy_Function call template - struct Handle_Return - { - template::type>::value>::type> - static Boxed_Value handle(T r) - { - return Boxed_Value(std::move(r), true); - } + struct Handle_Return { + template>>> + static Boxed_Value handle(T r) { + return Boxed_Value(std::move(r), true); + } - template::type>::value>::type> - static Boxed_Value handle(T &&r) - { - return Boxed_Value(std::make_shared(std::forward(r)), true); - } - }; + template>)>> + static Boxed_Value handle(T &&r) { + return Boxed_Value(std::make_shared(std::forward(r)), true); + } + }; template - struct Handle_Return &> - { - static Boxed_Value handle(const std::function &f) { - return Boxed_Value( - chaiscript::make_shared>>(f) - ); - } - }; + struct Handle_Return &> { + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + chaiscript::make_shared>>(f)); + } + }; template - struct Handle_Return> : Handle_Return &> - { - }; + struct Handle_Return> : Handle_Return &> { + }; template - struct Handle_Return>> - { - static Boxed_Value handle(const std::shared_ptr> &f) { - return Boxed_Value( - chaiscript::make_shared>(std::ref(*f),f) - ); - } - }; + struct Handle_Return>> { + static Boxed_Value handle(const std::shared_ptr> &f) { + return Boxed_Value( + chaiscript::make_shared>(std::ref(*f), f)); + } + }; template - struct Handle_Return> &> : Handle_Return>> - { - }; + struct Handle_Return> &> : Handle_Return>> { + }; template - struct Handle_Return>> : Handle_Return>> - { - }; + struct Handle_Return>> : Handle_Return>> { + }; template - struct Handle_Return &> - { - static Boxed_Value handle(std::function &f) { - return Boxed_Value( - chaiscript::make_shared>(std::ref(f), - std::shared_ptr>()) - ); - } + struct Handle_Return &> { + static Boxed_Value handle(std::function &f) { + return Boxed_Value(chaiscript::make_shared>( + std::ref(f), std::shared_ptr>())); + } - static Boxed_Value handle(const std::function &f) { - return Boxed_Value( - chaiscript::make_shared>>(f) - ); - } - }; + static Boxed_Value handle(const std::function &f) { + return Boxed_Value( + chaiscript::make_shared>>(f)); + } + }; template - struct Handle_Return - { - static Boxed_Value handle(Ret *p) - { - return Boxed_Value(p, true); - } - }; + struct Handle_Return { + static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); } + }; template - struct Handle_Return - { - static Boxed_Value handle(const Ret *p) - { - return Boxed_Value(p, true); - } - }; + struct Handle_Return { + static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); } + }; template - struct Handle_Return - { - static Boxed_Value handle(Ret *p) - { - return Boxed_Value(p, true); - } - }; + struct Handle_Return { + static Boxed_Value handle(Ret *p) { return Boxed_Value(p, true); } + }; template - struct Handle_Return - { - static Boxed_Value handle(const Ret *p) - { - return Boxed_Value(p, true); - } - }; + struct Handle_Return { + static Boxed_Value handle(const Ret *p) { return Boxed_Value(p, true); } + }; template - struct Handle_Return &> - { - static Boxed_Value handle(const std::shared_ptr &r) - { - return Boxed_Value(r, true); - } - }; + struct Handle_Return &> { + static Boxed_Value handle(const std::shared_ptr &r) { return Boxed_Value(r, true); } + }; template - struct Handle_Return> : Handle_Return &> - { - }; + struct Handle_Return> : Handle_Return &> { + }; template - struct Handle_Return &> : Handle_Return &> - { - }; - + struct Handle_Return &> : Handle_Return &> { + }; template - struct Handle_Return> : Handle_Return &> - { - static Boxed_Value handle(std::unique_ptr &&r) - { - return Boxed_Value(std::move(r), true); - } - }; - + struct Handle_Return> : Handle_Return &> { + static Boxed_Value handle(std::unique_ptr &&r) { return Boxed_Value(std::move(r), true); } + }; + template + struct Handle_Return_Ref { + template + static Boxed_Value handle(T &&r) { + return Boxed_Value(std::cref(r), true); + } + }; template - struct Handle_Return - { - static Boxed_Value handle(const Ret &r) - { - return Boxed_Value(std::cref(r), true); - } - }; + struct Handle_Return_Ref { + template + static Boxed_Value handle(T &&r) { + return Boxed_Value(typename std::remove_reference::type{r}, true); + } + }; template - struct Handle_Return - { - static Boxed_Value handle(Ret r) - { - return Boxed_Value(std::move(r)); - } - }; + struct Handle_Return : Handle_Return_Ref::type>::value> { + }; template - struct Handle_Return - { - static Boxed_Value handle(Ret &r) - { - return Boxed_Value(std::ref(r)); - } - }; + struct Handle_Return { + static Boxed_Value handle(Ret r) { return Boxed_Value(std::move(r)); } + }; + + template + struct Handle_Return { + static Boxed_Value handle(Ret &r) { return Boxed_Value(std::ref(r)); } + }; template<> - struct Handle_Return - { - static Boxed_Value handle(const Boxed_Value &r) - { - return r; - } - }; + struct Handle_Return { + static Boxed_Value handle(const Boxed_Value &r) noexcept { return r; } + }; template<> - struct Handle_Return : Handle_Return - { - }; + struct Handle_Return : Handle_Return { + }; template<> - struct Handle_Return : Handle_Return - { - }; + struct Handle_Return : Handle_Return { + }; template<> - struct Handle_Return : Handle_Return - { - }; + struct Handle_Return : Handle_Return { + }; /** - * Used internally for handling a return value from a Proxy_Function call - */ + * Used internally for handling a return value from a Proxy_Function call + */ template<> - struct Handle_Return - { - static Boxed_Value handle(const Boxed_Number &r) - { - return r.bv; - } - }; + struct Handle_Return { + static Boxed_Value handle(const Boxed_Number &r) noexcept { return r.bv; } + }; template<> - struct Handle_Return : Handle_Return - { - }; - + struct Handle_Return : Handle_Return { + }; /** - * Used internally for handling a return value from a Proxy_Function call - */ + * Used internally for handling a return value from a Proxy_Function call + */ template<> - struct Handle_Return - { - static Boxed_Value handle() - { - return void_var(); - } - }; - } - } -} + struct Handle_Return { + static Boxed_Value handle() { return void_var(); } + }; + } // namespace detail + } // namespace dispatch +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/operators.hpp b/include/chaiscript/dispatchkit/operators.hpp index 54c73b94..2545bfce 100644 --- a/include/chaiscript/dispatchkit/operators.hpp +++ b/include/chaiscript/dispatchkit/operators.hpp @@ -1,224 +1,184 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_OPERATORS_HPP_ #define CHAISCRIPT_OPERATORS_HPP_ #include "../chaiscript_defines.hpp" +#include "../dispatchkit/dispatchkit.hpp" #include "register_function.hpp" -namespace chaiscript -{ - namespace bootstrap - { - namespace operators - { - template - void assign(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs = rhs;}), "="); - } - - template - void assign_bitwise_and(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs &= rhs;}), "&="); - } - - template - void assign_xor(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs ^= rhs;}), "^="); - } - - template - void assign_bitwise_or(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs |= rhs;}), "|="); - } - - template - void assign_difference(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs -= rhs;}), "-="); - } - - template - void assign_left_shift(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "<<="); - } - - template - void assign_product(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs <<= rhs;}), "*="); - } - - template - void assign_quotient(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs /= rhs;}), "/="); - } - - template - void assign_remainder(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs %= rhs;}), "%="); - } - - template - void assign_right_shift(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs >>= rhs;}), ">>="); - } - - template - void assign_sum(Module& m) - { - m.add(chaiscript::fun([](T &lhs, const T&rhs)->T&{return lhs += rhs;}), "+="); - } - - template - void prefix_decrement(Module& m) - { - m.add(chaiscript::fun([](T &lhs)->T&{return --lhs;}), "--"); - } - - template - void prefix_increment(Module& m) - { - m.add(chaiscript::fun([](T &lhs)->T&{return ++lhs;}), "++"); - } - - template - void equal(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs==rhs;}), "=="); - } - - template - void greater_than(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>rhs;}), ">"); - } - - template - void greater_than_equal(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>=rhs;}), ">="); - } - - template - void less_than(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs - void less_than_equal(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs<=rhs;}), "<="); - } - - template - void logical_compliment(Module& m) - { - m.add(chaiscript::fun([](const T &lhs){return !lhs;}), "!"); - } - - template - void not_equal(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs!=rhs;}), "!="); - } - - template - void addition(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs+rhs;}), "+"); - } - - template - void unary_plus(Module& m) - { - m.add(chaiscript::fun([](const T &lhs){return +lhs;}), "+"); - } - - template - void subtraction(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs-rhs;}), "-"); - } - - template - void unary_minus(Module& m) - { - m.add(chaiscript::fun([](const T &lhs){return -lhs;}), "-"); - } - - template - void bitwise_and(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs&rhs;}), "&"); - } - - template - void bitwise_compliment(Module& m) - { - m.add(chaiscript::fun([](const T &lhs){return ~lhs;}), "~"); - } - - template - void bitwise_xor(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs^rhs;}), "^"); - } - - template - void bitwise_or(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs|rhs;}), "|"); - } - - template - void division(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs/rhs;}), "/"); - } - - template - void left_shift(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs< - void multiplication(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs*rhs;}), "*"); - } - - template - void remainder(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs%rhs;}), "%"); - } - - template - void right_shift(Module& m) - { - m.add(chaiscript::fun([](const T &lhs, const T &rhs){return lhs>>rhs;}), ">>"); - } - } +namespace chaiscript::bootstrap::operators { + template + void assign(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs = rhs; }), "="); } -} + + template + void assign_bitwise_and(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs &= rhs; }), "&="); + } + + template + void assign_xor(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs ^= rhs; }), "^="); + } + + template + void assign_bitwise_or(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs |= rhs; }), "|="); + } + + template + void assign_difference(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs -= rhs; }), "-="); + } + + template + void assign_left_shift(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "<<="); + } + + template + void assign_product(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs <<= rhs; }), "*="); + } + + template + void assign_quotient(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs /= rhs; }), "/="); + } + + template + void assign_remainder(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs %= rhs; }), "%="); + } + + template + void assign_right_shift(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs >>= rhs; }), ">>="); + } + + template + void assign_sum(Module &m) { + m.add(chaiscript::fun([](T &lhs, const T &rhs) -> T & { return lhs += rhs; }), "+="); + } + + template + void prefix_decrement(Module &m) { + m.add(chaiscript::fun([](T &lhs) -> T & { return --lhs; }), "--"); + } + + template + void prefix_increment(Module &m) { + m.add(chaiscript::fun([](T &lhs) -> T & { return ++lhs; }), "++"); + } + + template + void equal(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs == rhs; }), "=="); + } + + template + void greater_than(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs > rhs; }), ">"); + } + + template + void greater_than_equal(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >= rhs; }), ">="); + } + + template + void less_than(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs < rhs; }), "<"); + } + + template + void less_than_equal(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs <= rhs; }), "<="); + } + + template + void logical_compliment(Module &m) { + m.add(chaiscript::fun([](const T &lhs) { return !lhs; }), "!"); + } + + template + void not_equal(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs != rhs; }), "!="); + } + + template + void addition(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs + rhs; }), "+"); + } + + template + void unary_plus(Module &m) { + m.add(chaiscript::fun([](const T &lhs) { return +lhs; }), "+"); + } + + template + void subtraction(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs - rhs; }), "-"); + } + + template + void unary_minus(Module &m) { + m.add(chaiscript::fun([](const T &lhs) { return -lhs; }), "-"); + } + + template + void bitwise_and(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs & rhs; }), "&"); + } + + template + void bitwise_compliment(Module &m) { + m.add(chaiscript::fun([](const T &lhs) { return ~lhs; }), "~"); + } + + template + void bitwise_xor(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs ^ rhs; }), "^"); + } + + template + void bitwise_or(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs | rhs; }), "|"); + } + + template + void division(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs / rhs; }), "/"); + } + + template + void left_shift(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs << rhs; }), "<<"); + } + + template + void multiplication(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs * rhs; }), "*"); + } + + template + void remainder(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs % rhs; }), "%"); + } + + template + void right_shift(Module &m) { + m.add(chaiscript::fun([](const T &lhs, const T &rhs) { return lhs >> rhs; }), ">>"); + } +} // namespace chaiscript::bootstrap::operators #endif diff --git a/include/chaiscript/dispatchkit/proxy_constructors.hpp b/include/chaiscript/dispatchkit/proxy_constructors.hpp index bbe79d7a..b7e0d44a 100644 --- a/include/chaiscript/dispatchkit/proxy_constructors.hpp +++ b/include/chaiscript/dispatchkit/proxy_constructors.hpp @@ -1,41 +1,41 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ #define CHAISCRIPT_PROXY_CONSTRUCTORS_HPP_ #include "proxy_functions.hpp" -namespace chaiscript -{ - namespace dispatch - { - namespace detail - { +namespace chaiscript::dispatch::detail { + template + Proxy_Function build_constructor_(Class (*)(Params...)) { + if constexpr (!std::is_copy_constructible_v) { + auto call = [](auto &&...param) { return std::make_shared(std::forward(param)...); }; - template - Proxy_Function build_constructor_(Class (*)(Params...)) - { - auto call = dispatch::detail::Constructor(); + return Proxy_Function( + chaiscript::make_shared(Params...), decltype(call)>>(call)); + } else if constexpr (true) { + auto call = [](auto &&...param) { return Class(std::forward(param)...); }; - return Proxy_Function( - chaiscript::make_shared (Params...), decltype(call)>>(call)); - } + return Proxy_Function( + chaiscript::make_shared>( + call)); } } +} // namespace chaiscript::dispatch::detail - +namespace chaiscript { /// \brief Generates a constructor function for use with ChaiScript - /// + /// /// \tparam T The signature of the constructor to generate. In the form of: ClassType (ParamType1, ParamType2, ...) - /// + /// /// Example: /// \code /// chaiscript::ChaiScript chai; @@ -44,13 +44,10 @@ namespace chaiscript /// chai.add(constructor(), "MyClass"); /// \endcode template - Proxy_Function constructor() - { - T *f = nullptr; - return (dispatch::detail::build_constructor_(f)); - } - -} + Proxy_Function constructor() { + T *f = nullptr; + return (dispatch::detail::build_constructor_(f)); + } +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index 0c60315f..a481e2c6 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -1,106 +1,92 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_PROXY_FUNCTIONS_HPP_ #define CHAISCRIPT_PROXY_FUNCTIONS_HPP_ - #include #include +#include #include #include #include #include #include -#include #include "../chaiscript_defines.hpp" #include "boxed_cast.hpp" #include "boxed_value.hpp" +#include "dynamic_object.hpp" +#include "function_params.hpp" #include "proxy_functions_detail.hpp" #include "type_info.hpp" -#include "dynamic_object.hpp" namespace chaiscript { -class Type_Conversions; -namespace exception { -class bad_boxed_cast; -struct arity_error; -} // namespace exception -} // namespace chaiscript + class Type_Conversions; + namespace exception { + class bad_boxed_cast; + struct arity_error; + } // namespace exception +} // namespace chaiscript -namespace chaiscript -{ +namespace chaiscript { class Boxed_Number; struct AST_Node; - typedef std::unique_ptr AST_NodePtr; + using AST_NodePtr = std::unique_ptr; - namespace dispatch - { + namespace dispatch { template - std::function functor(std::shared_ptr func, const Type_Conversions_State *t_conversions); + std::function functor(std::shared_ptr func, const Type_Conversions_State *t_conversions); - class Param_Types - { - public: - Param_Types() - : m_has_types(false), - m_doti(user_type()) - {} + class Param_Types { + public: + Param_Types() + : m_has_types(false) { + } - explicit Param_Types(std::vector> t_types) - : m_types(std::move(t_types)), - m_has_types(false), - m_doti(user_type()) - { - update_has_types(); - } + explicit Param_Types(std::vector> t_types) + : m_types(std::move(t_types)) + , m_has_types(false) { + update_has_types(); + } - void push_front(std::string t_name, Type_Info t_ti) - { - m_types.emplace(m_types.begin(), std::move(t_name), t_ti); - update_has_types(); - } + void push_front(std::string t_name, Type_Info t_ti) { + m_types.emplace(m_types.begin(), std::move(t_name), t_ti); + update_has_types(); + } - bool operator==(const Param_Types &t_rhs) const - { - return m_types == t_rhs.m_types; - } + bool operator==(const Param_Types &t_rhs) const noexcept { return m_types == t_rhs.m_types; } - std::vector convert(std::vector vals, const Type_Conversions_State &t_conversions) const - { - for (size_t i = 0; i < vals.size(); ++i) - { - const auto &name = m_types[i].first; - if (!name.empty()) { - const auto &bv = vals[i]; + std::vector convert(Function_Params t_params, const Type_Conversions_State &t_conversions) const { + auto vals = t_params.to_vector(); + const auto dynamic_object_type_info = user_type(); + for (size_t i = 0; i < vals.size(); ++i) { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; - if (!bv.get_type_info().bare_equal(m_doti)) - { - const auto &ti = m_types[i].second; - if (!ti.is_undef()) - { - if (!bv.get_type_info().bare_equal(ti)) { - if (t_conversions->converts(ti, bv.get_type_info())) { + if (!bv.get_type_info().bare_equal(dynamic_object_type_info)) { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) { + if (!bv.get_type_info().bare_equal(ti)) { + if (t_conversions->converts(ti, bv.get_type_info())) { + try { + // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it + // either way, we are not responsible if it doesn't work + vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]); + } catch (...) { try { - // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it - // either way, we are not responsible if it doesn't work - vals[i] = t_conversions->boxed_type_conversion(m_types[i].second, t_conversions.saves(), vals[i]); - } catch (...) { - try { - // try going the other way - vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]); - } catch (const chaiscript::detail::exception::bad_any_cast &) { - throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info()); - } + // try going the other way + vals[i] = t_conversions->boxed_type_down_conversion(m_types[i].second, t_conversions.saves(), vals[i]); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), *m_types[i].second.bare_type_info()); } } } @@ -108,882 +94,728 @@ namespace chaiscript } } } - - return vals; } - // first result: is a match - // second result: needs conversions - std::pair match(const std::vector &vals, const Type_Conversions_State &t_conversions) const - { - bool needs_conversion = false; + return vals; + } - if (!m_has_types) { return std::make_pair(true, needs_conversion); } - if (vals.size() != m_types.size()) { return std::make_pair(false, needs_conversion); } + // first result: is a match + // second result: needs conversions + std::pair match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept { + const auto dynamic_object_type_info = user_type(); + bool needs_conversion = false; - for (size_t i = 0; i < vals.size(); ++i) - { - const auto &name = m_types[i].first; - if (!name.empty()) { - const auto &bv = vals[i]; + if (!m_has_types) { + return std::make_pair(true, needs_conversion); + } + if (vals.size() != m_types.size()) { + return std::make_pair(false, needs_conversion); + } - if (bv.get_type_info().bare_equal(m_doti)) - { - try { - const Dynamic_Object &d = boxed_cast(bv, &t_conversions); - if (!(name == "Dynamic_Object" || d.get_type_name() == name)) { - return std::make_pair(false, false); - } - } catch (const std::bad_cast &) { - return std::make_pair(false, false); - } - } else { - const auto &ti = m_types[i].second; - if (!ti.is_undef()) - { - if (!bv.get_type_info().bare_equal(ti)) { - if (!t_conversions->converts(ti, bv.get_type_info())) { - return std::make_pair(false, false); - } else { - needs_conversion = true; - } - } - } else { + for (size_t i = 0; i < vals.size(); ++i) { + const auto &name = m_types[i].first; + if (!name.empty()) { + const auto &bv = vals[i]; + + if (bv.get_type_info().bare_equal(dynamic_object_type_info)) { + try { + const Dynamic_Object &d = boxed_cast(bv, &t_conversions); + if (!(name == "Dynamic_Object" || d.get_type_name() == name)) { return std::make_pair(false, false); } + } catch (const std::bad_cast &) { + return std::make_pair(false, false); + } + } else { + const auto &ti = m_types[i].second; + if (!ti.is_undef()) { + if (!bv.get_type_info().bare_equal(ti)) { + if (!t_conversions->converts(ti, bv.get_type_info())) { + return std::make_pair(false, false); + } else { + needs_conversion = true; + } + } + } else { + return std::make_pair(false, false); } } } - - return std::make_pair(true, needs_conversion); } - const std::vector> &types() const - { - return m_types; - } + return std::make_pair(true, needs_conversion); + } - private: - void update_has_types() - { - for (const auto &type : m_types) - { - if (!type.first.empty()) - { - m_has_types = true; - return; - } + const std::vector> &types() const noexcept { return m_types; } + + private: + void update_has_types() { + for (const auto &type : m_types) { + if (!type.first.empty()) { + m_has_types = true; + return; } - - m_has_types = false; } - std::vector> m_types; - bool m_has_types; - Type_Info m_doti; + m_has_types = false; + } + std::vector> m_types; + bool m_has_types; }; /** - * Pure virtual base class for all Proxy_Function implementations - * Proxy_Functions are a type erasure of type safe C++ - * function calls. At runtime parameter types are expected to be - * tested against passed in types. - * Dispatch_Engine only knows how to work with Proxy_Function, no other - * function classes. - */ - class Proxy_Function_Base - { - public: - virtual ~Proxy_Function_Base() = default; + * Pure virtual base class for all Proxy_Function implementations + * Proxy_Functions are a type erasure of type safe C++ + * function calls. At runtime parameter types are expected to be + * tested against passed in types. + * Dispatch_Engine only knows how to work with Proxy_Function, no other + * function classes. + */ + class Proxy_Function_Base { + public: + virtual ~Proxy_Function_Base() = default; - Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const - { - if (m_arity < 0 || size_t(m_arity) == params.size()) { - return do_call(params, t_conversions); - } else { - throw exception::arity_error(static_cast(params.size()), m_arity); - } + Boxed_Value operator()(const Function_Params ¶ms, const chaiscript::Type_Conversions_State &t_conversions) const { + if (m_arity < 0 || size_t(m_arity) == params.size()) { + return do_call(params, t_conversions); + } else { + throw exception::arity_error(static_cast(params.size()), m_arity); } + } - /// Returns a vector containing all of the types of the parameters the function returns/takes - /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned - /// value contains exactly 1 Type_Info object: the return type - /// \returns the types of all parameters. - const std::vector &get_param_types() const { return m_types; } + /// Returns a vector containing all of the types of the parameters the function returns/takes + /// if the function is variadic or takes no arguments (arity of 0 or -1), the returned + /// value contains exactly 1 Type_Info object: the return type + /// \returns the types of all parameters. + const std::vector &get_param_types() const noexcept { return m_types; } - virtual bool operator==(const Proxy_Function_Base &) const = 0; - virtual bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; + virtual bool operator==(const Proxy_Function_Base &) const noexcept = 0; + virtual bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const = 0; - virtual bool is_attribute_function() const { return false; } + virtual bool is_attribute_function() const noexcept { return false; } - bool has_arithmetic_param() const - { - return m_has_arithmetic_param; - } + bool has_arithmetic_param() const noexcept { return m_has_arithmetic_param; } - virtual std::vector > get_contained_functions() const - { - return std::vector >(); - } + virtual std::vector> get_contained_functions() const { + return std::vector>(); + } - //! Return true if the function is a possible match - //! to the passed in values - bool filter(const std::vector &vals, const Type_Conversions_State &t_conversions) const - { - assert(m_arity == -1 || (m_arity > 0 && static_cast(vals.size()) == m_arity)); + //! Return true if the function is a possible match + //! to the passed in values + bool filter(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept { + assert(m_arity == -1 || (m_arity > 0 && static_cast(vals.size()) == m_arity)); - if (m_arity < 0) - { - return true; - } else if (m_arity > 1) { - return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); - } else { - return compare_type_to_param(m_types[1], vals[0], t_conversions); - } - } - - /// \returns the number of arguments the function takes or -1 if it is variadic - int get_arity() const - { - return m_arity; - } - - static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) - { - if (ti.is_undef() - || ti.bare_equal(user_type()) - || (!bv.get_type_info().is_undef() - && ( (ti.bare_equal(user_type()) && bv.get_type_info().is_arithmetic()) - || ti.bare_equal(bv.get_type_info()) - || bv.get_type_info().bare_equal(user_type >()) - || t_conversions->converts(ti, bv.get_type_info()) - ) - ) - ) - { - return true; - } else { - return false; - } - } - - virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const - { - return compare_type_to_param(m_types[1], bv, t_conversions); - } - - protected: - virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const = 0; - - Proxy_Function_Base(std::vector t_types, int t_arity) - : m_types(std::move(t_types)), m_arity(t_arity), m_has_arithmetic_param(false) - { - for (size_t i = 1; i < m_types.size(); ++i) - { - if (m_types[i].is_arithmetic()) - { - m_has_arithmetic_param = true; - return; - } - } - - } - - - static bool compare_types(const std::vector &tis, const std::vector &bvs, - const Type_Conversions_State &t_conversions) - { - if (tis.size() - 1 != bvs.size()) - { - return false; - } else { - const size_t size = bvs.size(); - for (size_t i = 0; i < size; ++i) - { - if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { return false; } - } - } + if (m_arity < 0) { return true; + } else if (m_arity > 1) { + return compare_type_to_param(m_types[1], vals[0], t_conversions) && compare_type_to_param(m_types[2], vals[1], t_conversions); + } else { + return compare_type_to_param(m_types[1], vals[0], t_conversions); } + } - std::vector m_types; - int m_arity; - bool m_has_arithmetic_param; + /// \returns the number of arguments the function takes or -1 if it is variadic + int get_arity() const noexcept { return m_arity; } + + static bool compare_type_to_param(const Type_Info &ti, const Boxed_Value &bv, const Type_Conversions_State &t_conversions) noexcept { + const auto boxed_value_ti = user_type(); + const auto boxed_number_ti = user_type(); + const auto function_ti = user_type>(); + + if (ti.is_undef() || ti.bare_equal(boxed_value_ti) + || (!bv.get_type_info().is_undef() + && ((ti.bare_equal(boxed_number_ti) && bv.get_type_info().is_arithmetic()) || ti.bare_equal(bv.get_type_info()) + || bv.get_type_info().bare_equal(function_ti) || t_conversions->converts(ti, bv.get_type_info())))) { + return true; + } else { + return false; + } + } + + virtual bool compare_first_type(const Boxed_Value &bv, const Type_Conversions_State &t_conversions) const noexcept { + /// TODO is m_types guaranteed to be at least 2?? + return compare_type_to_param(m_types[1], bv, t_conversions); + } + + protected: + virtual Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const = 0; + + Proxy_Function_Base(std::vector t_types, int t_arity) + : m_types(std::move(t_types)) + , m_arity(t_arity) + , m_has_arithmetic_param(false) { + for (size_t i = 1; i < m_types.size(); ++i) { + if (m_types[i].is_arithmetic()) { + m_has_arithmetic_param = true; + return; + } + } + } + + static bool compare_types(const std::vector &tis, const Function_Params &bvs, const Type_Conversions_State &t_conversions) noexcept { + if (tis.size() - 1 != bvs.size()) { + return false; + } else { + const size_t size = bvs.size(); + for (size_t i = 0; i < size; ++i) { + if (!compare_type_to_param(tis[i + 1], bvs[i], t_conversions)) { + return false; + } + } + } + return true; + } + + std::vector m_types; + int m_arity; + bool m_has_arithmetic_param; }; - } + } // namespace dispatch /// \brief Common typedef used for passing of any registered function in ChaiScript - typedef std::shared_ptr Proxy_Function; + using Proxy_Function = std::shared_ptr; /// \brief Const version of Proxy_Function. Points to a const Proxy_Function. This is how most registered functions /// are handled internally. - typedef std::shared_ptr Const_Proxy_Function; + using Const_Proxy_Function = std::shared_ptr; - namespace exception - { + namespace exception { /// \brief Exception thrown if a function's guard fails - class guard_error : public std::runtime_error - { - public: - guard_error() noexcept - : std::runtime_error("Guard evaluation failed") - { } + class guard_error : public std::runtime_error { + public: + guard_error() noexcept + : std::runtime_error("Guard evaluation failed") { + } - guard_error(const guard_error &) = default; + guard_error(const guard_error &) = default; - ~guard_error() noexcept override = default; + ~guard_error() noexcept override = default; }; - } + } // namespace exception - namespace dispatch - { - /** - * A Proxy_Function implementation that is not type safe, the called function - * is expecting a vector that it works with how it chooses. - */ - class Dynamic_Proxy_Function : public Proxy_Function_Base - { - public: - Dynamic_Proxy_Function( - const int t_arity, - std::shared_ptr t_parsenode, - Param_Types t_param_types = Param_Types(), - Proxy_Function t_guard = Proxy_Function()) - : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity), - m_param_types(std::move(t_param_types)), - m_guard(std::move(t_guard)), m_parsenode(std::move(t_parsenode)) - { - // assert(t_parsenode); - } + namespace dispatch { + /// A Proxy_Function implementation that is not type safe, the called function + /// is expecting a vector that it works with how it chooses. + class Dynamic_Proxy_Function : public Proxy_Function_Base { + public: + Dynamic_Proxy_Function(const int t_arity, + std::shared_ptr t_parsenode, + Param_Types t_param_types = Param_Types(), + Proxy_Function t_guard = Proxy_Function()) + : Proxy_Function_Base(build_param_type_list(t_param_types), t_arity) + , m_param_types(std::move(t_param_types)) + , m_guard(std::move(t_guard)) + , m_parsenode(std::move(t_parsenode)) { + // assert(t_parsenode); + } + bool operator==(const Proxy_Function_Base &rhs) const noexcept override { + const Dynamic_Proxy_Function *prhs = dynamic_cast(&rhs); - bool operator==(const Proxy_Function_Base &rhs) const override - { - const Dynamic_Proxy_Function *prhs = dynamic_cast(&rhs); - - return this == &rhs - || ((prhs != nullptr) - && this->m_arity == prhs->m_arity - && !this->m_guard && !prhs->m_guard + return this == &rhs + || ((prhs != nullptr) && this->m_arity == prhs->m_arity && !this->m_guard && !prhs->m_guard && this->m_param_types == prhs->m_param_types); + } + + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override { + return call_match_internal(vals, t_conversions).first; + } + + bool has_guard() const noexcept { return bool(m_guard); } + + Proxy_Function get_guard() const noexcept { return m_guard; } + + bool has_parse_tree() const noexcept { return static_cast(m_parsenode); } + + const AST_Node &get_parse_tree() const { + if (m_parsenode) { + return *m_parsenode; + } else { + throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree"); } + } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return call_match_internal(vals, t_conversions).first; + protected: + bool test_guard(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const { + if (m_guard) { + try { + return boxed_cast((*m_guard)(params, t_conversions)); + } catch (const exception::arity_error &) { + return false; + } catch (const exception::bad_boxed_cast &) { + return false; + } + } else { + return true; } + } - - Proxy_Function get_guard() const - { - return m_guard; - } - - bool has_parse_tree() const { - return static_cast(m_parsenode); - } - - const AST_Node &get_parse_tree() const - { - if (m_parsenode) { - return *m_parsenode; + // first result: is a match + // second result: needs conversions + std::pair call_match_internal(const Function_Params &vals, const Type_Conversions_State &t_conversions) const { + const auto comparison_result = [&]() { + if (m_arity < 0) { + return std::make_pair(true, false); + } else if (vals.size() == size_t(m_arity)) { + return m_param_types.match(vals, t_conversions); } else { - throw std::runtime_error("Dynamic_Proxy_Function does not have parse_tree"); + return std::make_pair(false, false); } - } + }(); + return std::make_pair(comparison_result.first && test_guard(vals, t_conversions), comparison_result.second); + } - protected: - bool test_guard(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const - { - if (m_guard) - { - try { - return boxed_cast((*m_guard)(params, t_conversions)); - } catch (const exception::arity_error &) { - return false; - } catch (const exception::bad_boxed_cast &) { - return false; - } + private: + static std::vector build_param_type_list(const Param_Types &t_types) { + // For the return type + std::vector types{chaiscript::detail::Get_Type_Info::get()}; + + for (const auto &t : t_types.types()) { + if (t.second.is_undef()) { + types.push_back(chaiscript::detail::Get_Type_Info::get()); } else { - return true; + types.push_back(t.second); } } - // first result: is a match - // second result: needs conversions - std::pair call_match_internal(const std::vector &vals, const Type_Conversions_State &t_conversions) const - { - const auto comparison_result = [&](){ - if (m_arity < 0) { - return std::make_pair(true, false); - } else if (vals.size() == size_t(m_arity)) { - return m_param_types.match(vals, t_conversions); - } else { - return std::make_pair(false, false); - } - }(); + return types; + } - return std::make_pair( - comparison_result.first && test_guard(vals, t_conversions), - comparison_result.second - ); - } + protected: + Param_Types m_param_types; - private: - static std::vector build_param_type_list(const Param_Types &t_types) - { - // For the return type - std::vector types{chaiscript::detail::Get_Type_Info::get()}; - - for (const auto &t : t_types.types()) - { - if (t.second.is_undef()) { - types.push_back(chaiscript::detail::Get_Type_Info::get()); - } else { - types.push_back(t.second); - } - } - - return types; - } - - protected: - Param_Types m_param_types; - - private: - Proxy_Function m_guard; - std::shared_ptr m_parsenode; + private: + Proxy_Function m_guard; + std::shared_ptr m_parsenode; }; - - template - class Dynamic_Proxy_Function_Impl final : public Dynamic_Proxy_Function - { - public: - Dynamic_Proxy_Function_Impl( - Callable t_f, - int t_arity=-1, - std::shared_ptr t_parsenode = AST_NodePtr(), - Param_Types t_param_types = Param_Types(), - Proxy_Function t_guard = Proxy_Function()) - : Dynamic_Proxy_Function( - t_arity, - std::move(t_parsenode), - std::move(t_param_types), - std::move(t_guard) - ), - m_f(std::move(t_f)) - { - } + class Dynamic_Proxy_Function_Impl final : public Dynamic_Proxy_Function { + public: + Dynamic_Proxy_Function_Impl(Callable t_f, + int t_arity = -1, + std::shared_ptr t_parsenode = AST_NodePtr(), + Param_Types t_param_types = Param_Types(), + Proxy_Function t_guard = Proxy_Function()) + : Dynamic_Proxy_Function(t_arity, std::move(t_parsenode), std::move(t_param_types), std::move(t_guard)) + , m_f(std::move(t_f)) { + } - - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - const auto match_results = call_match_internal(params, t_conversions); - if (match_results.first) - { - if (match_results.second) { - return m_f(m_param_types.convert(params, t_conversions)); - } else { - return m_f(params); - } + protected: + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + const auto [is_a_match, needs_conversions] = call_match_internal(params, t_conversions); + if (is_a_match) { + if (needs_conversions) { + return m_f(Function_Params{m_param_types.convert(params, t_conversions)}); } else { - throw exception::guard_error(); + return m_f(params); } + } else { + throw exception::guard_error(); } + } - private: - Callable m_f; + private: + Callable m_f; }; - template - Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg&& ... a) - { - return chaiscript::make_shared>( - std::forward(c), std::forward(a)...); + template + Proxy_Function make_dynamic_proxy_function(Callable &&c, Arg &&...a) { + return chaiscript::make_shared>(std::forward(c), + std::forward(a)...); } /// An object used by Bound_Function to represent "_" parameters /// of a binding. This allows for unbound parameters during bind. - struct Placeholder_Object - { + struct Placeholder_Object { }; /// An implementation of Proxy_Function that takes a Proxy_Function /// and substitutes bound parameters into the parameter list /// at runtime, when call() is executed. /// it is used for bind(function, param1, _, param2) style calls - class Bound_Function final : public Proxy_Function_Base - { - public: - Bound_Function(const Const_Proxy_Function &t_f, - const std::vector &t_args) - : Proxy_Function_Base(build_param_type_info(t_f, t_args), (t_f->get_arity()<0?-1:static_cast(build_param_type_info(t_f, t_args).size())-1)), - m_f(t_f), m_args(t_args) - { - assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast(m_args.size())); - } + class Bound_Function final : public Proxy_Function_Base { + public: + Bound_Function(const Const_Proxy_Function &t_f, const std::vector &t_args) + : Proxy_Function_Base(build_param_type_info(t_f, t_args), + (t_f->get_arity() < 0 ? -1 : static_cast(build_param_type_info(t_f, t_args).size()) - 1)) + , m_f(t_f) + , m_args(t_args) { + assert(m_f->get_arity() < 0 || m_f->get_arity() == static_cast(m_args.size())); + } - bool operator==(const Proxy_Function_Base &t_f) const override - { - return &t_f == this; - } + bool operator==(const Proxy_Function_Base &t_f) const noexcept override { return &t_f == this; } + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const override { + return m_f->call_match(Function_Params(build_param_list(vals)), t_conversions); + } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return m_f->call_match(build_param_list(vals), t_conversions); - } + std::vector get_contained_functions() const override { return std::vector{m_f}; } - std::vector get_contained_functions() const override - { - return std::vector{m_f}; - } + std::vector build_param_list(const Function_Params ¶ms) const { + auto parg = params.begin(); + auto barg = m_args.begin(); + std::vector args; - std::vector build_param_list(const std::vector ¶ms) const - { - auto parg = params.begin(); - auto barg = m_args.begin(); - - std::vector args; - - while (!(parg == params.end() && barg == m_args.end())) - { - while (barg != m_args.end() - && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info::get())) - { - args.push_back(*barg); - ++barg; - } - - if (parg != params.end()) - { - args.push_back(*parg); - ++parg; - } - - if (barg != m_args.end() - && barg->get_type_info() == chaiscript::detail::Get_Type_Info::get()) - { - ++barg; - } - } - return args; - } - - - protected: - static std::vector build_param_type_info(const Const_Proxy_Function &t_f, - const std::vector &t_args) - { - assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast(t_args.size())); - - if (t_f->get_arity() < 0) { return std::vector(); } - - const auto types = t_f->get_param_types(); - assert(types.size() == t_args.size() + 1); - - // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14 - std::vector retval{types[0]}; - - for (size_t i = 0; i < types.size() - 1; ++i) - { - if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info::get()) - { - retval.push_back(types[i+1]); - } + while (!(parg == params.end() && barg == m_args.end())) { + while (barg != m_args.end() && !(barg->get_type_info() == chaiscript::detail::Get_Type_Info::get())) { + args.push_back(*barg); + ++barg; } - return retval; + if (parg != params.end()) { + args.push_back(*parg); + ++parg; + } + + if (barg != m_args.end() && barg->get_type_info() == chaiscript::detail::Get_Type_Info::get()) { + ++barg; + } + } + return args; + } + + protected: + static std::vector build_param_type_info(const Const_Proxy_Function &t_f, const std::vector &t_args) { + assert(t_f->get_arity() < 0 || t_f->get_arity() == static_cast(t_args.size())); + + if (t_f->get_arity() < 0) { + return std::vector(); } - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - return (*m_f)(build_param_list(params), t_conversions); + const auto types = t_f->get_param_types(); + assert(types.size() == t_args.size() + 1); + + // this analysis warning is invalid in MSVC12 and doesn't exist in MSVC14 + std::vector retval{types[0]}; + + for (size_t i = 0; i < types.size() - 1; ++i) { + if (t_args[i].get_type_info() == chaiscript::detail::Get_Type_Info::get()) { + retval.push_back(types[i + 1]); + } } - private: - Const_Proxy_Function m_f; - std::vector m_args; + return retval; + } + + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + return (*m_f)(Function_Params{build_param_list(params)}, t_conversions); + } + + private: + Const_Proxy_Function m_f; + std::vector m_args; }; - class Proxy_Function_Impl_Base : public Proxy_Function_Base - { - public: - explicit Proxy_Function_Impl_Base(const std::vector &t_types) - : Proxy_Function_Base(t_types, static_cast(t_types.size()) - 1) - { - } + class Proxy_Function_Impl_Base : public Proxy_Function_Base { + public: + explicit Proxy_Function_Impl_Base(const std::vector &t_types) + : Proxy_Function_Base(t_types, static_cast(t_types.size()) - 1) { + } - bool call_match(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return static_cast(vals.size()) == get_arity() + bool call_match(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { + return static_cast(vals.size()) == get_arity() && (compare_types(m_types, vals, t_conversions) && compare_types_with_cast(vals, t_conversions)); - } + } - virtual bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const = 0; + virtual bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept = 0; }; - - /// For any callable object template - class Proxy_Function_Callable_Impl final : public Proxy_Function_Impl_Base - { - public: - explicit Proxy_Function_Callable_Impl(Callable f) - : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast(nullptr))), - m_f(std::move(f)) - { - } + class Proxy_Function_Callable_Impl final : public Proxy_Function_Impl_Base { + public: + explicit Proxy_Function_Callable_Impl(Callable f) + : Proxy_Function_Impl_Base(detail::build_param_type_list(static_cast(nullptr))) + , m_f(std::move(f)) { + } - bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); - } + bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { + return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); + } - bool operator==(const Proxy_Function_Base &t_func) const override - { - return dynamic_cast *>(&t_func) != nullptr; - } + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + return dynamic_cast *>(&t_func) != nullptr; + } + protected: + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + return detail::call_func(static_cast(nullptr), m_f, params, t_conversions); + } - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - return detail::call_func(detail::Function_Signature(), m_f, params, t_conversions); - } - - private: - Callable m_f; + private: + Callable m_f; }; + class Assignable_Proxy_Function : public Proxy_Function_Impl_Base { + public: + explicit Assignable_Proxy_Function(const std::vector &t_types) + : Proxy_Function_Impl_Base(t_types) { + } - class Assignable_Proxy_Function : public Proxy_Function_Impl_Base - { - public: - explicit Assignable_Proxy_Function(const std::vector &t_types) - : Proxy_Function_Impl_Base(t_types) - { - } - - virtual void assign(const std::shared_ptr &t_rhs) = 0; + virtual void assign(const std::shared_ptr &t_rhs) = 0; }; template - class Assignable_Proxy_Function_Impl final : public Assignable_Proxy_Function - { - public: - Assignable_Proxy_Function_Impl(std::reference_wrapper> t_f, std::shared_ptr> t_ptr) - : Assignable_Proxy_Function(detail::build_param_type_list(static_cast(nullptr))), - m_f(std::move(t_f)), m_shared_ptr_holder(std::move(t_ptr)) - { - assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); - } + class Assignable_Proxy_Function_Impl final : public Assignable_Proxy_Function { + public: + Assignable_Proxy_Function_Impl(std::reference_wrapper> t_f, std::shared_ptr> t_ptr) + : Assignable_Proxy_Function(detail::build_param_type_list(static_cast(nullptr))) + , m_f(std::move(t_f)) + , m_shared_ptr_holder(std::move(t_ptr)) { + assert(!m_shared_ptr_holder || m_shared_ptr_holder.get() == &m_f.get()); + } - bool compare_types_with_cast(const std::vector &vals, const Type_Conversions_State &t_conversions) const override - { - return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); - } + bool compare_types_with_cast(const Function_Params &vals, const Type_Conversions_State &t_conversions) const noexcept override { + return detail::compare_types_cast(static_cast(nullptr), vals, t_conversions); + } - bool operator==(const Proxy_Function_Base &t_func) const override - { - return dynamic_cast *>(&t_func) != nullptr; - } + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + return dynamic_cast *>(&t_func) != nullptr; + } - std::function internal_function() const - { - return m_f.get(); - } + std::function internal_function() const { return m_f.get(); } - void assign(const std::shared_ptr &t_rhs) override { - m_f.get() = dispatch::functor(t_rhs, nullptr); - } + void assign(const std::shared_ptr &t_rhs) override { m_f.get() = dispatch::functor(t_rhs, nullptr); } - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - return detail::call_func(detail::Function_Signature(), m_f.get(), params, t_conversions); - } + protected: + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + return detail::call_func(static_cast(nullptr), m_f.get(), params, t_conversions); + } - - private: - std::reference_wrapper> m_f; - std::shared_ptr> m_shared_ptr_holder; + private: + std::reference_wrapper> m_f; + std::shared_ptr> m_shared_ptr_holder; }; - /// Attribute getter Proxy_Function implementation template - class Attribute_Access final : public Proxy_Function_Base - { - public: - explicit Attribute_Access(T Class::* t_attr) - : Proxy_Function_Base(param_types(), 1), - m_attr(t_attr) - { + class Attribute_Access final : public Proxy_Function_Base { + public: + explicit Attribute_Access(T Class::*t_attr) + : Proxy_Function_Base(param_types(), 1) + , m_attr(t_attr) { + } + + bool is_attribute_function() const noexcept override { return true; } + + bool operator==(const Proxy_Function_Base &t_func) const noexcept override { + const Attribute_Access *aa = dynamic_cast *>(&t_func); + + if (aa) { + return m_attr == aa->m_attr; + } else { + return false; } + } - bool is_attribute_function() const override { return true; } - - bool operator==(const Proxy_Function_Base &t_func) const override - { - const Attribute_Access * aa - = dynamic_cast *>(&t_func); - - if (aa) { - return m_attr == aa->m_attr; - } else { - return false; - } + bool call_match(const Function_Params &vals, const Type_Conversions_State &) const noexcept override { + if (vals.size() != 1) { + return false; } + const auto class_type_info = user_type(); + return vals[0].get_type_info().bare_equal(class_type_info); + } - bool call_match(const std::vector &vals, const Type_Conversions_State &) const override - { - if (vals.size() != 1) - { - return false; - } - - return vals[0].get_type_info().bare_equal(user_type()); + protected: + Boxed_Value do_call(const Function_Params ¶ms, const Type_Conversions_State &t_conversions) const override { + const Boxed_Value &bv = params[0]; + if (bv.is_const()) { + const Class *o = boxed_cast(bv, &t_conversions); + return do_call_impl(o); + } else { + Class *o = boxed_cast(bv, &t_conversions); + return do_call_impl(o); } + } - protected: - Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions_State &t_conversions) const override - { - const Boxed_Value &bv = params[0]; - if (bv.is_const()) - { - const Class *o = boxed_cast(bv, &t_conversions); - return do_call_impl(o); - } else { - Class *o = boxed_cast(bv, &t_conversions); - return do_call_impl(o); - } - } - - private: - template - auto do_call_impl(Class *o) const -> std::enable_if_t::value, Boxed_Value> - { + private: + template + auto do_call_impl(Class *o) const { + if constexpr (std::is_pointer::value) { return detail::Handle_Return::handle(o->*m_attr); - } - - template - auto do_call_impl(const Class *o) const -> std::enable_if_t::value, Boxed_Value> - { - return detail::Handle_Return::handle(o->*m_attr); - } - - template - auto do_call_impl(Class *o) const -> std::enable_if_t::value, Boxed_Value> - { + } else { return detail::Handle_Return::type>::handle(o->*m_attr); } + } - template - auto do_call_impl(const Class *o) const -> std::enable_if_t::value, Boxed_Value> - { + template + auto do_call_impl(const Class *o) const { + if constexpr (std::is_pointer::value) { + return detail::Handle_Return::handle(o->*m_attr); + } else { return detail::Handle_Return::type>::type>::handle(o->*m_attr); } + } + static std::vector param_types() { return {user_type(), user_type()}; } - - static std::vector param_types() - { - return {user_type(), user_type()}; - } - - T Class::* m_attr; + std::vector m_param_types{user_type(), user_type()}; + T Class::*m_attr; }; - } + } // namespace dispatch - namespace exception - { - /// \brief Exception thrown in the case that a method dispatch fails - /// because no matching function was found - /// - /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast - /// exception - class dispatch_error : public std::runtime_error - { - public: - dispatch_error(std::vector t_parameters, - std::vector t_functions) - : std::runtime_error("Error with function dispatch"), parameters(std::move(t_parameters)), functions(std::move(t_functions)) - { - } + namespace exception { + /// \brief Exception thrown in the case that a method dispatch fails + /// because no matching function was found + /// + /// May be thrown due to an arity_error, a guard_error or a bad_boxed_cast + /// exception + class dispatch_error : public std::runtime_error { + public: + dispatch_error(const Function_Params &t_parameters, std::vector t_functions) + : std::runtime_error("Error with function dispatch") + , parameters(t_parameters.to_vector()) + , functions(std::move(t_functions)) { + } - dispatch_error(std::vector t_parameters, - std::vector t_functions, - const std::string &t_desc) - : std::runtime_error(t_desc), parameters(std::move(t_parameters)), 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()) + , functions(std::move(t_functions)) { + } + dispatch_error(const dispatch_error &) = default; + ~dispatch_error() noexcept override = default; - dispatch_error(const dispatch_error &) = default; - ~dispatch_error() noexcept override = default; - - std::vector parameters; - std::vector functions; + std::vector parameters; + std::vector functions; }; - } + } // namespace exception - namespace dispatch - { - namespace detail - { + namespace dispatch { + namespace detail { template - bool types_match_except_for_arithmetic(const FuncType &t_func, const std::vector &plist, - const Type_Conversions_State &t_conversions) - { - const std::vector &types = t_func->get_param_types(); + bool types_match_except_for_arithmetic(const FuncType &t_func, + const chaiscript::Function_Params &plist, + const Type_Conversions_State &t_conversions) noexcept { + const std::vector &types = t_func->get_param_types(); - if (t_func->get_arity() == -1) { return false; } - - assert(plist.size() == types.size() - 1); - - return std::mismatch(plist.begin(), plist.end(), - types.begin()+1, - [&](const Boxed_Value &bv, const Type_Info &ti) { - return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions) - || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic()); - } - ) == std::make_pair(plist.end(), types.end()); + if (t_func->get_arity() == -1) { + return false; } + assert(plist.size() == types.size() - 1); + + return std::mismatch(plist.begin(), + plist.end(), + types.begin() + 1, + [&](const Boxed_Value &bv, const Type_Info &ti) { + return Proxy_Function_Base::compare_type_to_param(ti, bv, t_conversions) + || (bv.get_type_info().is_arithmetic() && ti.is_arithmetic()); + }) + == std::make_pair(plist.end(), types.end()); + } + template - Boxed_Value dispatch_with_conversions(InItr begin, const InItr &end, const std::vector &plist, - const Type_Conversions_State &t_conversions, const Funcs &t_funcs) - { - InItr matching_func(end); + Boxed_Value dispatch_with_conversions(InItr begin, + const InItr &end, + const chaiscript::Function_Params &plist, + const Type_Conversions_State &t_conversions, + const Funcs &t_funcs) { + InItr matching_func(end); - while (begin != end) - { - if (types_match_except_for_arithmetic(begin->second, plist, t_conversions)) - { - if (matching_func == end) - { - matching_func = begin; + while (begin != end) { + if (types_match_except_for_arithmetic(begin->second, plist, t_conversions)) { + if (matching_func == end) { + matching_func = begin; + } else { + // handle const members vs non-const member, which is not really ambiguous + const auto &mat_fun_param_types = matching_func->second->get_param_types(); + const auto &next_fun_param_types = begin->second->get_param_types(); + + if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { + matching_func = begin; // keep the new one, the const/non-const matchup is correct + } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { + // keep the old one, it has a better const/non-const matchup } else { - // handle const members vs non-const member, which is not really ambiguous - const auto &mat_fun_param_types = matching_func->second->get_param_types(); - const auto &next_fun_param_types = begin->second->get_param_types(); - - if (plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { - matching_func = begin; // keep the new one, the const/non-const matchup is correct - } else if (!plist[0].is_const() && !mat_fun_param_types[1].is_const() && next_fun_param_types[1].is_const()) { - // keep the old one, it has a better const/non-const matchup - } else { - // ambiguous function call - throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); - } + // ambiguous function call + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); } } - - ++begin; } - if (matching_func == end) - { - // no appropriate function to attempt arithmetic type conversion on - throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); - } - - - std::vector newplist; - newplist.reserve(plist.size()); - - const std::vector &tis = matching_func->second->get_param_types(); - std::transform(tis.begin() + 1, tis.end(), - plist.begin(), - std::back_inserter(newplist), - [](const Type_Info &ti, const Boxed_Value ¶m) -> Boxed_Value { - if (ti.is_arithmetic() && param.get_type_info().is_arithmetic() - && param.get_type_info() != ti) { - return Boxed_Number(param).get_as(ti).bv; - } else { - return param; - } - } - ); - - try { - return (*(matching_func->second))(newplist, t_conversions); - } catch (const exception::bad_boxed_cast &) { - //parameter failed to cast - } catch (const exception::arity_error &) { - //invalid num params - } catch (const exception::guard_error &) { - //guard failed to allow the function to execute - } - - throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); - + ++begin; } - } + + if (matching_func == end) { + // no appropriate function to attempt arithmetic type conversion on + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); + } + + std::vector newplist; + newplist.reserve(plist.size()); + + const std::vector &tis = matching_func->second->get_param_types(); + std::transform(tis.begin() + 1, tis.end(), plist.begin(), std::back_inserter(newplist), [](const Type_Info &ti, const Boxed_Value ¶m) -> Boxed_Value { + if (ti.is_arithmetic() && param.get_type_info().is_arithmetic() && param.get_type_info() != ti) { + return Boxed_Number(param).get_as(ti).bv; + } else { + return param; + } + }); + + try { + return (*(matching_func->second))(chaiscript::Function_Params{newplist}, t_conversions); + } catch (const exception::bad_boxed_cast &) { + // parameter failed to cast + } catch (const exception::arity_error &) { + // invalid num params + } catch (const exception::guard_error &) { + // guard failed to allow the function to execute + } + + throw exception::dispatch_error(plist, std::vector(t_funcs.begin(), t_funcs.end())); + } + } // namespace detail /// Take a vector of functions and a vector of parameters. Attempt to execute /// each function against the set of parameters, in order, until a matching /// function is found or throw dispatch_error if no matching function is found template - Boxed_Value dispatch(const Funcs &funcs, - const std::vector &plist, const Type_Conversions_State &t_conversions) - { - std::vector> ordered_funcs; - ordered_funcs.reserve(funcs.size()); + Boxed_Value dispatch(const Funcs &funcs, const Function_Params &plist, const Type_Conversions_State &t_conversions) { + std::vector> ordered_funcs; + ordered_funcs.reserve(funcs.size()); - for (const auto &func : funcs) - { - const auto arity = func->get_arity(); + for (const auto &func : funcs) { + const auto arity = func->get_arity(); - if (arity == -1) - { - ordered_funcs.emplace_back(plist.size(), func.get()); - } else if (arity == static_cast(plist.size())) { - size_t numdiffs = 0; - for (size_t i = 0; i < plist.size(); ++i) - { - if (!func->get_param_types()[i+1].bare_equal(plist[i].get_type_info())) - { - ++numdiffs; - } - } - ordered_funcs.emplace_back(numdiffs, func.get()); - } - } - - - for (size_t i = 0; i <= plist.size(); ++i) - { - for (const auto &func : ordered_funcs ) - { - try { - if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions))) - { - return (*(func.second))(plist, t_conversions); - } - } catch (const exception::bad_boxed_cast &) { - //parameter failed to cast, try again - } catch (const exception::arity_error &) { - //invalid num params, try again - } catch (const exception::guard_error &) { - //guard failed to allow the function to execute, - //try again + if (arity == -1) { + ordered_funcs.emplace_back(plist.size(), func.get()); + } else if (arity == static_cast(plist.size())) { + size_t numdiffs = 0; + for (size_t i = 0; i < plist.size(); ++i) { + if (!func->get_param_types()[i + 1].bare_equal(plist[i].get_type_info())) { + ++numdiffs; } } + ordered_funcs.emplace_back(numdiffs, func.get()); } - - return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs); } - } -} + for (size_t i = 0; i <= plist.size(); ++i) { + for (const auto &func : ordered_funcs) { + try { + if (func.first == i && (i == 0 || func.second->filter(plist, t_conversions))) { + return (*(func.second))(plist, t_conversions); + } + } catch (const exception::bad_boxed_cast &) { + // parameter failed to cast, try again + } catch (const exception::arity_error &) { + // invalid num params, try again + } catch (const exception::guard_error &) { + // guard failed to allow the function to execute, + // try again + } + } + } + + return detail::dispatch_with_conversions(ordered_funcs.cbegin(), ordered_funcs.cend(), plist, t_conversions, funcs); + } + } // namespace dispatch +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp index 9ce58468..d43d4a13 100644 --- a/include/chaiscript/dispatchkit/proxy_functions_detail.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions_detail.hpp @@ -1,49 +1,45 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ #define CHAISCRIPT_PROXY_FUNCTIONS_DETAIL_HPP_ +#include #include #include #include -#include #include "../chaiscript_defines.hpp" #include "boxed_cast.hpp" #include "boxed_value.hpp" +#include "function_params.hpp" #include "handle_return.hpp" #include "type_info.hpp" -#include "callable_traits.hpp" namespace chaiscript { -class Type_Conversions_State; -namespace exception { -class bad_boxed_cast; -} // namespace exception -} // namespace chaiscript + class Type_Conversions_State; + namespace exception { + class bad_boxed_cast; + } // namespace exception +} // namespace chaiscript -namespace chaiscript -{ - namespace exception - { +namespace chaiscript { + namespace exception { /** - * Exception thrown when there is a mismatch in number of - * parameters during Proxy_Function execution - */ - struct arity_error : std::range_error - { + * Exception thrown when there is a mismatch in number of + * parameters during Proxy_Function execution + */ + struct arity_error : std::range_error { arity_error(int t_got, int t_expected) - : std::range_error("Function dispatch arity mismatch"), - got(t_got), expected(t_expected) - { + : std::range_error("Function dispatch arity mismatch") + , got(t_got) + , expected(t_expected) { } arity_error(const arity_error &) = default; @@ -53,87 +49,63 @@ namespace chaiscript int got; int expected; }; - } + } // namespace exception - namespace dispatch - { - namespace detail - { + namespace dispatch { + namespace detail { /** - * Used by Proxy_Function_Impl to return a list of all param types - * it contains. - */ - template - std::vector build_param_type_list(Ret (*)(Params...)) - { - /// \note somehow this is responsible for a large part of the code generation - return { user_type(), user_type()... }; - } - + * Used by Proxy_Function_Impl to return a list of all param types + * it contains. + */ + template + std::vector build_param_type_list(Ret (*)(Params...)) { + /// \note somehow this is responsible for a large part of the code generation + return {user_type(), user_type()...}; + } /** - * Used by Proxy_Function_Impl to determine if it is equivalent to another - * Proxy_Function_Impl object. This function is primarily used to prevent - * registration of two functions with the exact same signatures - */ - template - bool compare_types_cast(Ret (*)(Params...), - const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { - try { - std::vector::size_type i = 0; - (void)i; - (void)params; (void)t_conversions; - // this is ok because the order of evaluation of initializer lists is well defined - (void)std::initializer_list{(boxed_cast(params[i++], &t_conversions), 0)...}; - return true; - } catch (const exception::bad_boxed_cast &) { - return false; - } - } - - - template - Ret call_func(const chaiscript::dispatch::detail::Function_Signature &, - std::index_sequence, const Callable &f, - const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { - (void)params; (void)t_conversions; - return f(boxed_cast(params[I], &t_conversions)...); + * Used by Proxy_Function_Impl to determine if it is equivalent to another + * Proxy_Function_Impl object. This function is primarily used to prevent + * registration of two functions with the exact same signatures + */ + template + bool compare_types_cast(Ret (*)(Params...), const chaiscript::Function_Params ¶ms, const Type_Conversions_State &t_conversions) noexcept { + try { + std::vector::size_type i = 0; + (boxed_cast(params[i++], &t_conversions), ...); + return true; + } catch (const exception::bad_boxed_cast &) { + return false; } + } + template + Ret call_func(Ret (*)(Params...), + std::index_sequence, + const Callable &f, + [[maybe_unused]] const chaiscript::Function_Params ¶ms, + [[maybe_unused]] const Type_Conversions_State &t_conversions) { + return f(boxed_cast(params[I], &t_conversions)...); + } /// Used by Proxy_Function_Impl to perform typesafe execution of a function. /// The function attempts to unbox each parameter to the expected type. /// if any unboxing fails the execution of the function fails and /// the bad_boxed_cast is passed up to the caller. - template - Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, - const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { + 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)); } + } - template - Boxed_Value call_func(const chaiscript::dispatch::detail::Function_Signature &sig, const Callable &f, - const std::vector ¶ms, const Type_Conversions_State &t_conversions) - { - call_func(sig, std::index_sequence_for{}, f, params, t_conversions); -#ifdef CHAISCRIPT_MSVC -#pragma warning(push) -#pragma warning(disable : 4702) -#endif - // MSVC is reporting that this is unreachable code - and it's wrong. - return Handle_Return::handle(); -#ifdef CHAISCRIPT_MSVC -#pragma warning(pop) -#endif - } - - } - } - -} + } // namespace detail + } // namespace dispatch +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/register_function.hpp b/include/chaiscript/dispatchkit/register_function.hpp index 2e56fe0e..a22280a9 100644 --- a/include/chaiscript/dispatchkit/register_function.hpp +++ b/include/chaiscript/dispatchkit/register_function.hpp @@ -1,23 +1,61 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_REGISTER_FUNCTION_HPP_ #define CHAISCRIPT_REGISTER_FUNCTION_HPP_ #include #include "bind_first.hpp" +#include "function_signature.hpp" #include "proxy_functions.hpp" -namespace chaiscript -{ +namespace chaiscript { + namespace dispatch::detail { + template + Param1 get_first_param(Function_Params, Obj &&obj) { + return static_cast(std::forward(obj)); + } + + template + auto make_callable_impl(Func &&func, Function_Signature, Is_Noexcept, Is_Member, Is_MemberObject, Is_Object>) { + if constexpr (Is_MemberObject) { + // we now that the Param pack will have only one element, so we are safe expanding it here + return Proxy_Function(chaiscript::make_shared...>>( + std::forward(func))); + } else if constexpr (Is_Member) { + // TODO some kind of bug is preventing forwarding of this noexcept for the lambda + auto call = [func = std::forward(func)](auto &&obj, auto &&...param) /* noexcept(Is_Noexcept) */ -> decltype(auto) { + return ((get_first_param(Function_Params{}, obj).*func)(std::forward(param)...)); + }; + return Proxy_Function( + chaiscript::make_shared>( + std::move(call))); + } else { + return Proxy_Function( + chaiscript::make_shared>>( + std::forward(func))); + } + } + + // this version peels off the function object itself from the function signature, when used + // on a callable object + template + auto make_callable(Func &&func, Function_Signature, Is_Noexcept, false, false, true>) { + return make_callable_impl(std::forward(func), Function_Signature, Is_Noexcept, false, false, true>{}); + } + + template + auto make_callable(Func &&func, Function_Signature, Is_Noexcept, Is_Member, Is_MemberObject, false> fs) { + return make_callable_impl(std::forward(func), fs); + } + } // namespace dispatch::detail /// \brief Creates a new Proxy_Function object from a free function, member function or data member /// \param[in] t Function / member to expose @@ -31,95 +69,19 @@ namespace chaiscript /// void memberfunction(); /// int memberdata; /// }; - /// + /// /// chaiscript::ChaiScript chai; /// chai.add(fun(&myfunction), "myfunction"); /// chai.add(fun(&MyClass::memberfunction), "memberfunction"); /// chai.add(fun(&MyClass::memberdata), "memberdata"); /// \endcode - /// + /// /// \sa \ref adding_functions template - Proxy_Function fun(const T &t) - { - typedef typename dispatch::detail::Callable_Traits::Signature Signature; + Proxy_Function fun(T &&t) { + return dispatch::detail::make_callable(std::forward(t), dispatch::detail::function_signature(t)); + } - return Proxy_Function( - chaiscript::make_shared>(t)); - } - - - template - Proxy_Function fun(Ret (*func)(Param...)) - { - auto fun_call = dispatch::detail::Fun_Caller(func); - - return Proxy_Function( - chaiscript::make_shared>(fun_call)); - - } - - template - Proxy_Function fun(Ret (Class::*t_func)(Param...) const) - { - auto call = dispatch::detail::Const_Caller(t_func); - - return Proxy_Function( - chaiscript::make_shared>(call)); - } - - template - Proxy_Function fun(Ret (Class::*t_func)(Param...)) - { - auto call = dispatch::detail::Caller(t_func); - - return Proxy_Function( - chaiscript::make_shared>(call)); - - } - - template::value>::type*/> - Proxy_Function fun(T Class::* m /*, typename std::enable_if::value>::type* = 0*/ ) - { - return Proxy_Function(chaiscript::make_shared>(m)); - } - -// only compile this bit if noexcept is part of the type system -// -#if (defined(__cpp_noexcept_function_type) && __cpp_noexcept_function_type >= 201510) || (defined(_NOEXCEPT_TYPES_SUPPORTED) && _MSC_VER >= 1912) - template - Proxy_Function fun(Ret (*func)(Param...) noexcept) - { - auto fun_call = dispatch::detail::Fun_Caller(func); - - return Proxy_Function( - chaiscript::make_shared>(fun_call)); - - } - - template - Proxy_Function fun(Ret (Class::*t_func)(Param...) const noexcept) - { - auto call = dispatch::detail::Const_Caller(t_func); - - return Proxy_Function( - chaiscript::make_shared>(call)); - } - - template - Proxy_Function fun(Ret (Class::*t_func)(Param...) noexcept) - { - auto call = dispatch::detail::Caller(t_func); - - return Proxy_Function( - chaiscript::make_shared>(call)); - - } -#endif - - - - /// \brief Creates a new Proxy_Function object from a free function, member function or data member and binds the first parameter of it /// \param[in] t Function / member to expose /// \param[in] q Value to bind to first parameter @@ -130,23 +92,19 @@ namespace chaiscript /// { /// void memberfunction(int); /// }; - /// + /// /// MyClass obj; /// chaiscript::ChaiScript chai; /// // Add function taking only one argument, an int, and permanently bound to "obj" - /// chai.add(fun(&MyClass::memberfunction, std::ref(obj)), "memberfunction"); + /// chai.add(fun(&MyClass::memberfunction, std::ref(obj)), "memberfunction"); /// \endcode - /// + /// /// \sa \ref adding_functions template - Proxy_Function fun(T &&t, const Q &q) - { - return fun(detail::bind_first(std::forward(t), q)); - } - - -} + Proxy_Function fun(T &&t, const Q &q) { + return fun(detail::bind_first(std::forward(t), q)); + } +} // namespace chaiscript #endif - diff --git a/include/chaiscript/dispatchkit/short_alloc.hpp b/include/chaiscript/dispatchkit/short_alloc.hpp index e1cc9a96..64f57446 100644 --- a/include/chaiscript/dispatchkit/short_alloc.hpp +++ b/include/chaiscript/dispatchkit/short_alloc.hpp @@ -2,19 +2,19 @@ #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 @@ -23,137 +23,115 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -#include #include +#include -template -class arena -{ - alignas(alignment) char buf_[N]; - char* ptr_; +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; + ~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; + 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_;} + 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);} + 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;} + 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; - } +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)); + 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 +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 -{ +template +class short_alloc { public: - using value_type = T; - static auto constexpr alignment = Align; - static auto constexpr size = N; - using arena_type = arena; + using value_type = T; + static auto constexpr alignment = Align; + static auto constexpr size = N; + using arena_type = arena; private: - arena_type& a_; + arena_type &a_; public: - short_alloc(const short_alloc&) = default; - short_alloc& operator=(const short_alloc&) = delete; + 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_) {} + 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>;}; + 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)); - } + 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 bool operator==(const short_alloc &x, const short_alloc &y) noexcept; - template friend class short_alloc; + 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 N == M && A1 == A2 && &x.a_ == &y.a_; } -template -inline -bool -operator!=(const short_alloc& x, const short_alloc& y) noexcept -{ - return !(x == y); +template +inline bool operator!=(const short_alloc &x, const short_alloc &y) noexcept { + return !(x == y); } -#endif // SHORT_ALLOC_HPP - +#endif // SHORT_ALLOC_HPP diff --git a/include/chaiscript/dispatchkit/type_conversions.hpp b/include/chaiscript/dispatchkit/type_conversions.hpp index d9d2f374..e47afbf7 100644 --- a/include/chaiscript/dispatchkit/type_conversions.hpp +++ b/include/chaiscript/dispatchkit/type_conversions.hpp @@ -1,16 +1,16 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ #define CHAISCRIPT_DYNAMIC_CAST_CONVERSION_HPP_ +#include #include #include #include @@ -20,522 +20,419 @@ #include #include "../chaiscript_threading.hpp" +#include "../utility/static_string.hpp" #include "bad_boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" #include "type_info.hpp" -namespace chaiscript -{ - namespace exception - { - class bad_boxed_dynamic_cast : public bad_boxed_cast - { - public: - bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, - const std::string &t_what) noexcept - : bad_boxed_cast(t_from, t_to, t_what) - { - } +namespace chaiscript { + namespace exception { + /// \brief Error thrown when there's a problem with type conversion + class conversion_error : public bad_boxed_cast { + public: + conversion_error(const Type_Info t_to, const Type_Info t_from, const utility::Static_String what) noexcept + : bad_boxed_cast(t_from, (*t_to.bare_type_info()), what) + , type_to(t_to) { + } - bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept - : bad_boxed_cast(t_from, t_to) - { - } - - explicit bad_boxed_dynamic_cast(const std::string &w) noexcept - : bad_boxed_cast(w) - { - } - - bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default; - - ~bad_boxed_dynamic_cast() noexcept override = default; + Type_Info type_to; }; - class bad_boxed_type_cast : public bad_boxed_cast - { - public: - bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to, - const std::string &t_what) noexcept - : bad_boxed_cast(t_from, t_to, t_what) - { - } + class bad_boxed_dynamic_cast : public bad_boxed_cast { + public: + bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to, const utility::Static_String &t_what) noexcept + : bad_boxed_cast(t_from, t_to, t_what) { + } - bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept - : bad_boxed_cast(t_from, t_to) - { - } + bad_boxed_dynamic_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept + : bad_boxed_cast(t_from, t_to) { + } - explicit bad_boxed_type_cast(const std::string &w) noexcept - : bad_boxed_cast(w) - { - } + explicit bad_boxed_dynamic_cast(const utility::Static_String &w) noexcept + : bad_boxed_cast(w) { + } - bad_boxed_type_cast(const bad_boxed_type_cast &) = default; - - ~bad_boxed_type_cast() noexcept override = default; - }; - } - - - namespace detail - { - class Type_Conversion_Base - { - public: - virtual Boxed_Value convert(const Boxed_Value &from) const = 0; - virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0; - - const Type_Info &to() const - { - return m_to; - } - const Type_Info &from() const - { - return m_from; - } - - virtual bool bidir() const - { - return true; - } - - virtual ~Type_Conversion_Base() = default; - - protected: - Type_Conversion_Base(Type_Info t_to, Type_Info t_from) - : m_to(std::move(t_to)), m_from(std::move(t_from)) - { - } - - - private: - const Type_Info m_to; - const Type_Info m_from; + bad_boxed_dynamic_cast(const bad_boxed_dynamic_cast &) = default; + ~bad_boxed_dynamic_cast() noexcept override = default; }; - template - class Static_Caster - { - public: - static Boxed_Value cast(const Boxed_Value &t_from) - { - if (t_from.get_type_info().bare_equal(chaiscript::user_type())) - { - if (t_from.is_pointer()) - { - // Dynamic cast out the contained boxed value, which we know is the type we want - if (t_from.is_const()) - { - return Boxed_Value( - [&](){ - if (auto data = std::static_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) - { - return data; - } else { - throw std::bad_cast(); - } - }() - ); + class bad_boxed_type_cast : public bad_boxed_cast { + public: + bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to, const utility::Static_String &t_what) noexcept + : bad_boxed_cast(t_from, t_to, t_what) { + } + + bad_boxed_type_cast(const Type_Info &t_from, const std::type_info &t_to) noexcept + : bad_boxed_cast(t_from, t_to) { + } + + explicit bad_boxed_type_cast(const utility::Static_String &w) noexcept + : bad_boxed_cast(w) { + } + + bad_boxed_type_cast(const bad_boxed_type_cast &) = default; + + ~bad_boxed_type_cast() noexcept override = default; + }; + } // namespace exception + + namespace detail { + class Type_Conversion_Base { + public: + virtual Boxed_Value convert(const Boxed_Value &from) const = 0; + virtual Boxed_Value convert_down(const Boxed_Value &to) const = 0; + + const Type_Info &to() const noexcept { return m_to; } + const Type_Info &from() const noexcept { return m_from; } + + virtual bool bidir() const noexcept { return true; } + + virtual ~Type_Conversion_Base() = default; + + protected: + Type_Conversion_Base(Type_Info t_to, Type_Info t_from) + : m_to(std::move(t_to)) + , m_from(std::move(t_from)) { + } + + private: + const Type_Info m_to; + const Type_Info m_from; + }; + + template + class Static_Caster { + public: + static Boxed_Value cast(const Boxed_Value &t_from) { + if (t_from.get_type_info().bare_equal(chaiscript::user_type())) { + if (t_from.is_pointer()) { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) { + return Boxed_Value([&]() { + if (auto data + = std::static_pointer_cast(detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; } else { - return Boxed_Value( - [&](){ - if (auto data = std::static_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) - { - return data; - } else { - throw std::bad_cast(); - } - }() - ); + throw std::bad_cast(); } - } else { - // Pull the reference out of the contained boxed value, which we know is the type we want - if (t_from.is_const()) - { - const From &d = detail::Cast_Helper::cast(t_from, nullptr); - const To &data = static_cast(d); - return Boxed_Value(std::cref(data)); - } else { - From &d = detail::Cast_Helper::cast(t_from, nullptr); - To &data = static_cast(d); - return Boxed_Value(std::ref(data)); - } - } + }()); } else { - throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + return Boxed_Value([&]() { + if (auto data = std::static_pointer_cast(detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; + } else { + throw std::bad_cast(); + } + }()); + } + } else { + // Pull the reference out of the contained boxed value, which we know is the type we want + if (t_from.is_const()) { + const From &d = detail::Cast_Helper::cast(t_from, nullptr); + const To &data = static_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = detail::Cast_Helper::cast(t_from, nullptr); + To &data = static_cast(d); + return Boxed_Value(std::ref(data)); } } - - }; + } else { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + } + } + }; - - template - class Dynamic_Caster - { - public: - static Boxed_Value cast(const Boxed_Value &t_from) - { - if (t_from.get_type_info().bare_equal(chaiscript::user_type())) - { - if (t_from.is_pointer()) - { - // Dynamic cast out the contained boxed value, which we know is the type we want - if (t_from.is_const()) - { - return Boxed_Value( - [&](){ - if (auto data = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) - { - return data; - } else { - throw std::bad_cast(); - } - }() - ); + template + class Dynamic_Caster { + public: + static Boxed_Value cast(const Boxed_Value &t_from) { + if (t_from.get_type_info().bare_equal(chaiscript::user_type())) { + if (t_from.is_pointer()) { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) { + return Boxed_Value([&]() { + if (auto data + = std::dynamic_pointer_cast(detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; + } else { + throw std::bad_cast(); + } + }()); + } else { + return Boxed_Value([&]() { + if (auto data = std::dynamic_pointer_cast(detail::Cast_Helper>::cast(t_from, nullptr))) { + return data; } else { - return Boxed_Value( - [&](){ - if (auto data = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr))) - { - return data; - } else { #ifdef CHAISCRIPT_LIBCPP - /// \todo fix this someday after libc++ is fixed. - if (std::string(typeid(To).name()).find("Assignable_Proxy_Function") != std::string::npos) { - auto from = detail::Cast_Helper >::cast(t_from, nullptr); - if (std::string(typeid(*from).name()).find("Assignable_Proxy_Function_Impl") != std::string::npos) { - return std::static_pointer_cast(from); - } - } + /// \todo fix this someday after libc++ is fixed. + if (std::string(typeid(To).name()).find("Assignable_Proxy_Function") != std::string::npos) { + auto from = detail::Cast_Helper>::cast(t_from, nullptr); + if (std::string(typeid(*from).name()).find("Assignable_Proxy_Function_Impl") != std::string::npos) { + return std::static_pointer_cast(from); + } + } #endif - throw std::bad_cast(); - } - }() - ); + throw std::bad_cast(); } - } else { - // Pull the reference out of the contained boxed value, which we know is the type we want - if (t_from.is_const()) - { - const From &d = detail::Cast_Helper::cast(t_from, nullptr); - const To &data = dynamic_cast(d); - return Boxed_Value(std::cref(data)); - } else { - From &d = detail::Cast_Helper::cast(t_from, nullptr); - To &data = dynamic_cast(d); - return Boxed_Value(std::ref(data)); - } - } + }()); + } + } else { + // Pull the reference out of the contained boxed value, which we know is the type we want + if (t_from.is_const()) { + const From &d = detail::Cast_Helper::cast(t_from, nullptr); + const To &data = dynamic_cast(d); + return Boxed_Value(std::cref(data)); } else { - throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + From &d = detail::Cast_Helper::cast(t_from, nullptr); + To &data = dynamic_cast(d); + return Boxed_Value(std::ref(data)); } } - - }; - - - template - class Dynamic_Conversion_Impl : public Type_Conversion_Base - { - public: - Dynamic_Conversion_Impl() - : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) - { - } - - Boxed_Value convert_down(const Boxed_Value &t_base) const override - { - return Dynamic_Caster::cast(t_base); - } - - Boxed_Value convert(const Boxed_Value &t_derived) const override - { - return Static_Caster::cast(t_derived); + } else { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); } + } }; template - class Static_Conversion_Impl : public Type_Conversion_Base - { - public: - Static_Conversion_Impl() - : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) - { - } + class Dynamic_Conversion_Impl : public Type_Conversion_Base { + public: + Dynamic_Conversion_Impl() + : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) { + } - Boxed_Value convert_down(const Boxed_Value &t_base) const override - { - throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), typeid(Derived), - "Unable to cast down inheritance hierarchy with non-polymorphic types"); - } + Boxed_Value convert_down(const Boxed_Value &t_base) const override { return Dynamic_Caster::cast(t_base); } - bool bidir() const override - { - return false; - } - - Boxed_Value convert(const Boxed_Value &t_derived) const override - { - return Static_Caster::cast(t_derived); - } + Boxed_Value convert(const Boxed_Value &t_derived) const override { return Static_Caster::cast(t_derived); } }; + template + class Static_Conversion_Impl : public Type_Conversion_Base { + public: + Static_Conversion_Impl() + : Type_Conversion_Base(chaiscript::user_type(), chaiscript::user_type()) { + } + Boxed_Value convert_down(const Boxed_Value &t_base) const override { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_base.get_type_info(), + typeid(Derived), + "Unable to cast down inheritance hierarchy with non-polymorphic types"); + } + + bool bidir() const noexcept override { return false; } + + Boxed_Value convert(const Boxed_Value &t_derived) const override { return Static_Caster::cast(t_derived); } + }; template - class Type_Conversion_Impl : public Type_Conversion_Base - { - public: - Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func) - : Type_Conversion_Base(t_to, t_from), - m_func(std::move(t_func)) - { - } + class Type_Conversion_Impl : public Type_Conversion_Base { + public: + Type_Conversion_Impl(Type_Info t_from, Type_Info t_to, Callable t_func) + : Type_Conversion_Base(t_to, t_from) + , m_func(std::move(t_func)) { + } - Boxed_Value convert_down(const Boxed_Value &) const override - { - throw chaiscript::exception::bad_boxed_type_cast("No conversion exists"); - } + Boxed_Value convert_down(const Boxed_Value &) const override { + throw chaiscript::exception::bad_boxed_type_cast("No conversion exists"); + } - Boxed_Value convert(const Boxed_Value &t_from) const override - { - /// \todo better handling of errors from the conversion function - return m_func(t_from); - } + Boxed_Value convert(const Boxed_Value &t_from) const override { + /// \todo better handling of errors from the conversion function + return m_func(t_from); + } - bool bidir() const override - { - return false; - } + bool bidir() const noexcept override { return false; } - - private: - Callable m_func; + private: + Callable m_func; }; - } + } // namespace detail - class Type_Conversions - { - public: - struct Conversion_Saves - { - bool enabled = false; - std::vector saves; - }; + class Type_Conversions { + public: + struct Conversion_Saves { + bool enabled = false; + std::vector saves; + }; - struct Less_Than - { - bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const - { - return *t_lhs != *t_rhs && t_lhs->before(*t_rhs); - } - }; + struct Less_Than { + bool operator()(const std::type_info *t_lhs, const std::type_info *t_rhs) const noexcept { + return *t_lhs != *t_rhs && t_lhs->before(*t_rhs); + } + }; - Type_Conversions() - : m_mutex(), - m_conversions(), - m_convertableTypes(), - m_num_types(0) - { + Type_Conversions() + : m_mutex() + , m_conversions() + , m_convertableTypes() + , m_num_types(0) { + } + + Type_Conversions(const Type_Conversions &t_other) = delete; + Type_Conversions(Type_Conversions &&) = delete; + + Type_Conversions &operator=(const Type_Conversions &) = delete; + Type_Conversions &operator=(Type_Conversions &&) = delete; + + const std::set &thread_cache() const { + auto &cache = *m_thread_cache; + if (cache.size() != m_num_types) { + chaiscript::detail::threading::shared_lock l(m_mutex); + cache = m_convertableTypes; } - Type_Conversions(const Type_Conversions &t_other) = delete; - Type_Conversions(Type_Conversions &&) = default; + return cache; + } - Type_Conversions &operator=(const Type_Conversions &) = delete; - Type_Conversions &operator=(Type_Conversions &&) = default; - - const std::set &thread_cache() const - { - auto &cache = *m_thread_cache; - if (cache.size() != m_num_types) - { - chaiscript::detail::threading::shared_lock l(m_mutex); - cache = m_convertableTypes; - } - - return cache; + void add_conversion(const std::shared_ptr &conversion) { + chaiscript::detail::threading::unique_lock l(m_mutex); + if (find_bidir(conversion->to(), conversion->from()) != m_conversions.end()) { + throw exception::conversion_error(conversion->to(), conversion->from(), "Trying to re-insert an existing conversion!"); } + m_conversions.insert(conversion); + m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()}); + m_num_types = m_convertableTypes.size(); + } - void add_conversion(const std::shared_ptr &conversion) - { - chaiscript::detail::threading::unique_lock l(m_mutex); - /// \todo error if a conversion already exists - m_conversions.insert(conversion); - m_convertableTypes.insert({conversion->to().bare_type_info(), conversion->from().bare_type_info()}); - m_num_types = m_convertableTypes.size(); + template + bool convertable_type() const noexcept { + const auto type = user_type().bare_type_info(); + return thread_cache().count(type) != 0; + } + + template + bool converts() const noexcept { + return converts(user_type(), user_type()); + } + + bool converts(const Type_Info &to, const Type_Info &from) const noexcept { + const auto &types = thread_cache(); + if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0) { + return has_conversion(to, from); + } else { + return false; } + } - template - bool convertable_type() const - { - return thread_cache().count(user_type().bare_type_info()) != 0; + template + Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const { + return boxed_type_conversion(user_type(), t_saves, from); + } + + template + Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const { + return boxed_type_down_conversion(user_type(), t_saves, to); + } + + Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const { + try { + Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from); + if (t_saves.enabled) { + t_saves.saves.push_back(ret); } - - template - bool converts() const - { - return converts(user_type(), user_type()); - } - - bool converts(const Type_Info &to, const Type_Info &from) const - { - const auto &types = thread_cache(); - if (types.count(to.bare_type_info()) != 0 && types.count(from.bare_type_info()) != 0) - { - return has_conversion(to, from); - } else { - return false; - } - } - - template - Boxed_Value boxed_type_conversion(Conversion_Saves &t_saves, const Boxed_Value &from) const - { - return boxed_type_conversion(user_type(), t_saves, from); - } - - template - Boxed_Value boxed_type_down_conversion(Conversion_Saves &t_saves, const Boxed_Value &to) const - { - return boxed_type_down_conversion(user_type(), t_saves, to); - } - - - Boxed_Value boxed_type_conversion(const Type_Info &to, Conversion_Saves &t_saves, const Boxed_Value &from) const - { - try { - Boxed_Value ret = get_conversion(to, from.get_type_info())->convert(from); - if (t_saves.enabled) { t_saves.saves.push_back(ret); } - return ret; - } catch (const std::out_of_range &) { - throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion"); - } catch (const std::bad_cast &) { - throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation"); - } - } - - Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const - { - try { - Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to); - if (t_saves.enabled) { t_saves.saves.push_back(ret); } - return ret; - } catch (const std::out_of_range &) { - throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion"); - } catch (const std::bad_cast &) { - throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation"); - } - } - - static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) - { - t_saves.enabled = t_val; - } - - std::vector take_saves(Conversion_Saves &t_saves) - { - std::vector ret; - std::swap(ret, t_saves.saves); return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(from.get_type_info(), *to.bare_type_info(), "Unable to perform dynamic_cast operation"); } + } - bool has_conversion(const Type_Info &to, const Type_Info &from) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); - return find_bidir(to, from) != m_conversions.end(); - } - - std::shared_ptr get_conversion(const Type_Info &to, const Type_Info &from) const - { - chaiscript::detail::threading::shared_lock l(m_mutex); - - const auto itr = find(to, from); - - if (itr != m_conversions.end()) - { - return *itr; - } else { - throw std::out_of_range("No such conversion exists from " + from.bare_name() + " to " + to.bare_name()); + Boxed_Value boxed_type_down_conversion(const Type_Info &from, Conversion_Saves &t_saves, const Boxed_Value &to) const { + try { + Boxed_Value ret = get_conversion(to.get_type_info(), from)->convert_down(to); + if (t_saves.enabled) { + t_saves.saves.push_back(ret); } + return ret; + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(to.get_type_info(), *from.bare_type_info(), "Unable to perform dynamic_cast operation"); } + } - Conversion_Saves &conversion_saves() const { - return *m_conversion_saves; + static void enable_conversion_saves(Conversion_Saves &t_saves, bool t_val) { t_saves.enabled = t_val; } + + std::vector take_saves(Conversion_Saves &t_saves) { + std::vector ret; + std::swap(ret, t_saves.saves); + return ret; + } + + bool has_conversion(const Type_Info &to, const Type_Info &from) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + return find_bidir(to, from) != m_conversions.end(); + } + + std::shared_ptr get_conversion(const Type_Info &to, const Type_Info &from) const { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const auto itr = find(to, from); + + if (itr != m_conversions.end()) { + return *itr; + } else { + throw std::out_of_range(std::string("No such conversion exists from ") + from.bare_name() + " to " + to.bare_name()); } + } - private: - std::set >::const_iterator find_bidir( - const Type_Info &to, const Type_Info &from) const - { - return std::find_if(m_conversions.begin(), m_conversions.end(), - [&to, &from](const std::shared_ptr &conversion) -> bool - { - return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from)) - || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from)); - } - ); - } + Conversion_Saves &conversion_saves() const noexcept { return *m_conversion_saves; } - std::set >::const_iterator find( - const Type_Info &to, const Type_Info &from) const - { - return std::find_if(m_conversions.begin(), m_conversions.end(), - [&to, &from](const std::shared_ptr &conversion) - { - return conversion->to().bare_equal(to) && conversion->from().bare_equal(from); - } - ); - } + private: + std::set>::const_iterator find_bidir(const Type_Info &to, const Type_Info &from) const { + return std::find_if(m_conversions.begin(), + m_conversions.end(), + [&to, &from](const std::shared_ptr &conversion) -> bool { + return (conversion->to().bare_equal(to) && conversion->from().bare_equal(from)) + || (conversion->bidir() && conversion->from().bare_equal(to) && conversion->to().bare_equal(from)); + }); + } - std::set> get_conversions() const - { - chaiscript::detail::threading::shared_lock l(m_mutex); + std::set>::const_iterator find(const Type_Info &to, const Type_Info &from) const { + return std::find_if(m_conversions.begin(), + m_conversions.end(), + [&to, &from](const std::shared_ptr &conversion) { + return conversion->to().bare_equal(to) && conversion->from().bare_equal(from); + }); + } - return m_conversions; - } + std::set> get_conversions() const { + chaiscript::detail::threading::shared_lock l(m_mutex); + return m_conversions; + } - - mutable chaiscript::detail::threading::shared_mutex m_mutex; - std::set> m_conversions; - std::set m_convertableTypes; - std::atomic_size_t m_num_types; - mutable chaiscript::detail::threading::Thread_Storage> m_thread_cache; - mutable chaiscript::detail::threading::Thread_Storage m_conversion_saves; + mutable chaiscript::detail::threading::shared_mutex m_mutex; + std::set> m_conversions; + std::set m_convertableTypes; + std::atomic_size_t m_num_types; + mutable chaiscript::detail::threading::Thread_Storage> m_thread_cache; + mutable chaiscript::detail::threading::Thread_Storage m_conversion_saves; }; - class Type_Conversions_State - { - public: - Type_Conversions_State(const Type_Conversions &t_conversions, - Type_Conversions::Conversion_Saves &t_saves) - : m_conversions(t_conversions), - m_saves(t_saves) - { - } + class Type_Conversions_State { + public: + Type_Conversions_State(const Type_Conversions &t_conversions, Type_Conversions::Conversion_Saves &t_saves) + : m_conversions(t_conversions) + , m_saves(t_saves) { + } - const Type_Conversions *operator->() const { - return &m_conversions.get(); - } + const Type_Conversions *operator->() const noexcept { return &m_conversions.get(); } - const Type_Conversions *get() const { - return &m_conversions.get(); - } + const Type_Conversions *get() const noexcept { return &m_conversions.get(); } - Type_Conversions::Conversion_Saves &saves() const { - return m_saves; - } + Type_Conversions::Conversion_Saves &saves() const noexcept { return m_saves; } - private: - std::reference_wrapper m_conversions; - std::reference_wrapper m_saves; + private: + std::reference_wrapper m_conversions; + std::reference_wrapper m_saves; }; - typedef std::shared_ptr Type_Conversion; + using Type_Conversion = std::shared_ptr; /// \brief Used to register a to / parent class relationship with ChaiScript. Necessary if you /// want automatic conversions up your inheritance hierarchy. @@ -557,93 +454,86 @@ namespace chaiscript /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::to_class()); /// \endcode - /// + /// template - Type_Conversion base_class(typename std::enable_if::value && std::is_polymorphic::value>::type* = nullptr) - { - //Can only be used with related polymorphic types - //may be expanded some day to support conversions other than child -> parent - static_assert(std::is_base_of::value, "Classes are not related by inheritance"); + Type_Conversion base_class() { + // Can only be used with related polymorphic types + // may be expanded some day to support conversions other than child -> parent + static_assert(std::is_base_of::value, "Classes are not related by inheritance"); - return chaiscript::make_shared>(); + if constexpr (std::is_polymorphic::value && std::is_polymorphic::value) { + return chaiscript::make_shared>(); + } else { + return chaiscript::make_shared>(); + } } - template - Type_Conversion base_class(typename std::enable_if::value || !std::is_polymorphic::value>::type* = nullptr) - { - //Can only be used with related polymorphic types - //may be expanded some day to support conversions other than child -> parent - static_assert(std::is_base_of::value, "Classes are not related by inheritance"); - - return chaiscript::make_shared>(); - } - - template - Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, - const Callable &t_func) - { - return chaiscript::make_shared>(t_from, t_to, t_func); - } + Type_Conversion type_conversion(const Type_Info &t_from, const Type_Info &t_to, const Callable &t_func) { + return chaiscript::make_shared>(t_from, t_to, t_func); + } template - Type_Conversion type_conversion(const Callable &t_function) - { - auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value { - // not even attempting to call boxed_cast so that we don't get caught in some call recursion - return chaiscript::Boxed_Value(t_function(detail::Cast_Helper::cast(t_bv, nullptr))); - }; + Type_Conversion type_conversion(const Callable &t_function) { + auto func = [t_function](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in some call recursion + return chaiscript::Boxed_Value(t_function(detail::Cast_Helper::cast(t_bv, nullptr))); + }; - return chaiscript::make_shared>(user_type(), user_type(), func); - } + return chaiscript::make_shared>(user_type(), + user_type(), + func); + } template - Type_Conversion type_conversion() - { - static_assert(std::is_convertible::value, "Types are not automatically convertible"); - auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { - // not even attempting to call boxed_cast so that we don't get caught in some call recursion - return chaiscript::Boxed_Value(To(detail::Cast_Helper::cast(t_bv, nullptr))); - }; + Type_Conversion type_conversion() { + static_assert(std::is_convertible::value, "Types are not automatically convertible"); + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + // not even attempting to call boxed_cast so that we don't get caught in some call recursion + return chaiscript::Boxed_Value(To(detail::Cast_Helper::cast(t_bv, nullptr))); + }; - return chaiscript::make_shared>(user_type(), user_type(), func); - } + return chaiscript::make_shared>(user_type(), + user_type(), + func); + } template - Type_Conversion vector_conversion() - { - auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { - const std::vector &from_vec = detail::Cast_Helper &>::cast(t_bv, nullptr); + Type_Conversion vector_conversion() { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::vector &from_vec = detail::Cast_Helper &>::cast(t_bv, nullptr); - To vec; - vec.reserve(from_vec.size()); - for (const Boxed_Value &bv : from_vec) { - vec.push_back(detail::Cast_Helper::cast(bv, nullptr)); - } + To vec; + vec.reserve(from_vec.size()); + for (const Boxed_Value &bv : from_vec) { + vec.push_back(detail::Cast_Helper::cast(bv, nullptr)); + } - return Boxed_Value(std::move(vec)); - }; + return Boxed_Value(std::move(vec)); + }; - return chaiscript::make_shared>(user_type>(), user_type(), func); - } + return chaiscript::make_shared>(user_type>(), + user_type(), + func); + } template - Type_Conversion map_conversion() - { - auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { - const std::map &from_map = detail::Cast_Helper &>::cast(t_bv, nullptr); + Type_Conversion map_conversion() { + auto func = [](const Boxed_Value &t_bv) -> Boxed_Value { + const std::map &from_map + = detail::Cast_Helper &>::cast(t_bv, nullptr); - To map; - for (const std::pair &p : from_map) { - map.insert(std::make_pair(p.first, detail::Cast_Helper::cast(p.second, nullptr))); - } + To map; + for (const std::pair &p : from_map) { + map.insert(std::make_pair(p.first, detail::Cast_Helper::cast(p.second, nullptr))); + } - return Boxed_Value(std::move(map)); - }; - - return chaiscript::make_shared>(user_type>(), user_type(), func); - } -} + return Boxed_Value(std::move(map)); + }; + return chaiscript::make_shared>( + user_type>(), user_type(), func); + } +} // namespace chaiscript #endif diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 53f007b0..2f21ee58 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -1,244 +1,210 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_TYPE_INFO_HPP_ #define CHAISCRIPT_TYPE_INFO_HPP_ #include +#include #include #include -#include -namespace chaiscript -{ - - namespace detail - { +namespace chaiscript { + namespace detail { template - struct Bare_Type - { - typedef typename std::remove_cv::type>::type>::type type; - }; - } - + struct Bare_Type { + using type = typename std::remove_cv::type>::type>::type; + }; + } // namespace detail /// \brief Compile time deduced information about a type - class Type_Info - { - public: - constexpr Type_Info(const bool t_is_const, const bool t_is_reference, const bool t_is_pointer, const bool t_is_void, - const bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) - : m_type_info(t_ti), m_bare_type_info(t_bare_ti), - m_flags((static_cast(t_is_const) << is_const_flag) - + (static_cast(t_is_reference) << is_reference_flag) - + (static_cast(t_is_pointer) << is_pointer_flag) - + (static_cast(t_is_void) << is_void_flag) - + (static_cast(t_is_arithmetic) << is_arithmetic_flag)) - { + class Type_Info { + public: + constexpr Type_Info(const bool t_is_const, + const bool t_is_reference, + const bool t_is_pointer, + const bool t_is_void, + const bool t_is_arithmetic, + const std::type_info *t_ti, + const std::type_info *t_bare_ti) noexcept + : m_type_info(t_ti) + , m_bare_type_info(t_bare_ti) + , m_flags((static_cast(t_is_const) << is_const_flag) + (static_cast(t_is_reference) << is_reference_flag) + + (static_cast(t_is_pointer) << is_pointer_flag) + (static_cast(t_is_void) << is_void_flag) + + (static_cast(t_is_arithmetic) << is_arithmetic_flag)) { + } + + constexpr Type_Info() noexcept = default; + + bool operator<(const Type_Info &ti) const noexcept { return m_type_info->before(*ti.m_type_info); } + + constexpr bool operator!=(const Type_Info &ti) const noexcept { return !(operator==(ti)); } + + constexpr bool operator!=(const std::type_info &ti) const noexcept { return !(operator==(ti)); } + + constexpr bool operator==(const Type_Info &ti) const noexcept { + return ti.m_type_info == m_type_info || *ti.m_type_info == *m_type_info; + } + + constexpr bool operator==(const std::type_info &ti) const noexcept { return !is_undef() && (*m_type_info) == ti; } + + constexpr bool bare_equal(const Type_Info &ti) const noexcept { + return ti.m_bare_type_info == m_bare_type_info || *ti.m_bare_type_info == *m_bare_type_info; + } + + constexpr bool bare_equal_type_info(const std::type_info &ti) const noexcept { return !is_undef() && (*m_bare_type_info) == ti; } + + constexpr bool is_const() const noexcept { return (m_flags & (1 << is_const_flag)) != 0; } + constexpr bool is_reference() const noexcept { return (m_flags & (1 << is_reference_flag)) != 0; } + constexpr bool is_void() const noexcept { return (m_flags & (1 << is_void_flag)) != 0; } + constexpr bool is_arithmetic() const noexcept { return (m_flags & (1 << is_arithmetic_flag)) != 0; } + constexpr bool is_undef() const noexcept { return (m_flags & (1 << is_undef_flag)) != 0; } + constexpr bool is_pointer() const noexcept { return (m_flags & (1 << is_pointer_flag)) != 0; } + + const char *name() const noexcept { + if (!is_undef()) { + return m_type_info->name(); + } else { + return ""; } + } - constexpr Type_Info() = default; - - bool operator<(const Type_Info &ti) const noexcept - { - return m_type_info->before(*ti.m_type_info); + const char *bare_name() const noexcept { + if (!is_undef()) { + return m_bare_type_info->name(); + } else { + return ""; } + } - constexpr bool operator!=(const Type_Info &ti) const noexcept - { - return !(operator==(ti)); - } + constexpr const std::type_info *bare_type_info() const noexcept { return m_bare_type_info; } - constexpr bool operator!=(const std::type_info &ti) const noexcept - { - return !(operator==(ti)); - } + private: + struct Unknown_Type { + }; - constexpr bool operator==(const Type_Info &ti) const noexcept - { - return ti.m_type_info == m_type_info - || *ti.m_type_info == *m_type_info; - } - - constexpr bool operator==(const std::type_info &ti) const noexcept - { - return !is_undef() && (*m_type_info) == ti; - } - - constexpr bool bare_equal(const Type_Info &ti) const noexcept - { - return ti.m_bare_type_info == m_bare_type_info - || *ti.m_bare_type_info == *m_bare_type_info; - } - - constexpr bool bare_equal_type_info(const std::type_info &ti) const noexcept - { - return !is_undef() && (*m_bare_type_info) == ti; - } - - constexpr bool is_const() const noexcept { return (m_flags & (1 << is_const_flag)) != 0; } - constexpr bool is_reference() const noexcept { return (m_flags & (1 << is_reference_flag)) != 0; } - constexpr bool is_void() const noexcept { return (m_flags & (1 << is_void_flag)) != 0; } - constexpr bool is_arithmetic() const noexcept { return (m_flags & (1 << is_arithmetic_flag)) != 0; } - constexpr bool is_undef() const noexcept { return (m_flags & (1 << is_undef_flag)) != 0; } - constexpr bool is_pointer() const noexcept { return (m_flags & (1 << is_pointer_flag)) != 0; } - - std::string name() const - { - if (!is_undef()) - { - return m_type_info->name(); - } else { - return ""; - } - } - - std::string bare_name() const - { - if (!is_undef()) - { - return m_bare_type_info->name(); - } else { - return ""; - } - } - - constexpr const std::type_info *bare_type_info() const - { - return m_bare_type_info; - } - - private: - struct Unknown_Type {}; - - const std::type_info *m_type_info = &typeid(Unknown_Type); - const std::type_info *m_bare_type_info = &typeid(Unknown_Type); - static const int is_const_flag = 0; - static const int is_reference_flag = 1; - static const int is_pointer_flag = 2; - static const int is_void_flag = 3; - static const int is_arithmetic_flag = 4; - static const int is_undef_flag = 5; - unsigned int m_flags = (1 << is_undef_flag); + const std::type_info *m_type_info = &typeid(Unknown_Type); + const std::type_info *m_bare_type_info = &typeid(Unknown_Type); + static const int is_const_flag = 0; + static const int is_reference_flag = 1; + static const int is_pointer_flag = 2; + static const int is_void_flag = 3; + static const int is_arithmetic_flag = 4; + static const int is_undef_flag = 5; + unsigned int m_flags = (1 << is_undef_flag); }; - namespace detail - { + namespace detail { /// Helper used to create a Type_Info object template - struct Get_Type_Info - { - static constexpr Type_Info get() - { - return Type_Info(std::is_const::type>::type>::value, - std::is_reference::value, std::is_pointer::value, - std::is_void::value, - (std::is_arithmetic::value || std::is_arithmetic::type>::value) - && !std::is_same::type>::type, bool>::value, - &typeid(T), - &typeid(typename Bare_Type::type)); - } - }; + struct Get_Type_Info { + constexpr static Type_Info get() noexcept { + return Type_Info(std::is_const::type>::type>::value, + std::is_reference::value, + std::is_pointer::value, + std::is_void::value, + (std::is_arithmetic::value || std::is_arithmetic::type>::value) + && !std::is_same::type>::type, bool>::value, + &typeid(T), + &typeid(typename Bare_Type::type)); + } + }; template - struct Get_Type_Info > - { -// typedef T type; - - static constexpr Type_Info get() - { - return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, - std::is_void::value, - std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, - &typeid(std::shared_ptr ), - &typeid(typename Bare_Type::type)); - } - }; + struct Get_Type_Info> { + constexpr static Type_Info get() noexcept { + return Type_Info(std::is_const::value, + std::is_reference::value, + std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value + && !std::is_same::type>::type, bool>::value, + &typeid(std::shared_ptr), + &typeid(typename Bare_Type::type)); + } + }; template - struct Get_Type_Info &> : Get_Type_Info> - { - }; + struct Get_Type_Info &> : Get_Type_Info> { + }; template - struct Get_Type_Info &> - { - static constexpr Type_Info get() - { - return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, - std::is_void::value, - std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, - &typeid(const std::shared_ptr &), - &typeid(typename Bare_Type::type)); - } - }; + struct Get_Type_Info &> { + constexpr static Type_Info get() noexcept { + return Type_Info(std::is_const::value, + std::is_reference::value, + std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value + && !std::is_same::type>::type, bool>::value, + &typeid(const std::shared_ptr &), + &typeid(typename Bare_Type::type)); + } + }; template - struct Get_Type_Info > - { - static constexpr Type_Info get() - { - return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, - std::is_void::value, - std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, - &typeid(std::reference_wrapper ), - &typeid(typename Bare_Type::type)); - } - }; + struct Get_Type_Info> { + constexpr static Type_Info get() noexcept { + return Type_Info(std::is_const::value, + std::is_reference::value, + std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value + && !std::is_same::type>::type, bool>::value, + &typeid(std::reference_wrapper), + &typeid(typename Bare_Type::type)); + } + }; template - struct Get_Type_Info &> - { - static constexpr Type_Info get() - { - return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, - std::is_void::value, - std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, - &typeid(const std::reference_wrapper &), - &typeid(typename Bare_Type::type)); - } - }; + struct Get_Type_Info &> { + constexpr static Type_Info get() noexcept { + return Type_Info(std::is_const::value, + std::is_reference::value, + std::is_pointer::value, + std::is_void::value, + std::is_arithmetic::value + && !std::is_same::type>::type, bool>::value, + &typeid(const std::reference_wrapper &), + &typeid(typename Bare_Type::type)); + } + }; - } + } // namespace detail /// \brief Creates a Type_Info object representing the type passed in /// \tparam T Type of object to get a Type_Info for, derived from the passed in parameter /// \return Type_Info for T - /// + /// /// \b Example: /// \code /// int i; /// chaiscript::Type_Info ti = chaiscript::user_type(i); /// \endcode template - constexpr Type_Info user_type(const T &/*t*/) - { + constexpr Type_Info user_type(const T & /*t*/) noexcept { return detail::Get_Type_Info::get(); } - /// \brief Creates a Type_Info object representing the templated type /// \tparam T Type of object to get a Type_Info for /// \return Type_Info for T - /// + /// /// \b Example: /// \code /// chaiscript::Type_Info ti = chaiscript::user_type(); /// \endcode template - constexpr Type_Info user_type() - { + constexpr Type_Info user_type() noexcept { return detail::Get_Type_Info::get(); } -} +} // namespace chaiscript #endif - diff --git a/include/chaiscript/language/chaiscript_algebraic.hpp b/include/chaiscript/language/chaiscript_algebraic.hpp index 8a49d2ee..08592802 100644 --- a/include/chaiscript/language/chaiscript_algebraic.hpp +++ b/include/chaiscript/language/chaiscript_algebraic.hpp @@ -1,108 +1,167 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_ALGEBRAIC_HPP_ #define CHAISCRIPT_ALGEBRAIC_HPP_ -#include "../utility/fnv1a.hpp" +#include "../utility/hash.hpp" #include -namespace chaiscript -{ - +namespace chaiscript { struct Operators { - enum class Opers - { - boolean_flag, - equals, less_than, greater_than, less_than_equal, greater_than_equal, not_equal, - non_const_flag, - assign, pre_increment, pre_decrement, assign_product, assign_sum, - assign_quotient, assign_difference, - non_const_int_flag, - assign_bitwise_and, assign_bitwise_or, assign_shift_left, assign_shift_right, - assign_remainder, assign_bitwise_xor, - const_int_flag, - shift_left, shift_right, remainder, bitwise_and, bitwise_or, bitwise_xor, bitwise_complement, - const_flag, - sum, quotient, product, difference, unary_plus, unary_minus, + enum class Opers { + equals, + less_than, + greater_than, + less_than_equal, + greater_than_equal, + not_equal, + assign, + pre_increment, + pre_decrement, + assign_product, + assign_sum, + assign_quotient, + assign_difference, + assign_bitwise_and, + assign_bitwise_or, + assign_shift_left, + assign_shift_right, + assign_remainder, + assign_bitwise_xor, + shift_left, + shift_right, + remainder, + bitwise_and, + bitwise_or, + bitwise_xor, + bitwise_complement, + sum, + quotient, + product, + difference, + unary_plus, + unary_minus, invalid }; - static const char *to_string(Opers t_oper) { - static const char *opers[] = { - "", - "==", "<", ">", "<=", ">=", "!=", - "", - "=", "++", "--", "*=", "+=", - "/=", "-=", - "", - "&=", "|=", "<<=", ">>=", - "%=", "^=", - "", - "<<", ">>", "%", "&", "|", "^", "~", - "", - "+", "/", "*", "-", "+", "-", - "" - }; + constexpr static const char *to_string(Opers t_oper) noexcept { + constexpr const char *opers[] + = {"", "==", "<", ">", "<=", ">=", "!=", "", "=", "++", "--", "*=", "+=", "/=", "-=", "", "&=", "|=", "<<=", ">>=", "%=", "^=", "", "<<", ">>", "%", "&", "|", "^", "~", "", "+", "/", "*", "-", "+", "-", ""}; return opers[static_cast(t_oper)]; } - static Opers to_operator(const std::string &t_str, bool t_is_unary = false) - { + constexpr static Opers to_operator(std::string_view t_str, bool t_is_unary = false) noexcept { #ifdef CHAISCRIPT_MSVC #pragma warning(push) #pragma warning(disable : 4307) #endif - const auto op_hash = utility::fnv1a_32(t_str.c_str()); + const auto op_hash = utility::hash(t_str); switch (op_hash) { - case utility::fnv1a_32("=="): { return Opers::equals; } - case utility::fnv1a_32("<"): { return Opers::less_than; } - case utility::fnv1a_32(">"): { return Opers::greater_than; } - case utility::fnv1a_32("<="): { return Opers::less_than_equal; } - case utility::fnv1a_32(">="): { return Opers::greater_than_equal; } - case utility::fnv1a_32("!="): { return Opers::not_equal; } - case utility::fnv1a_32("="): { return Opers::assign; } - case utility::fnv1a_32("++"): { return Opers::pre_increment; } - case utility::fnv1a_32("--"): { return Opers::pre_decrement; } - case utility::fnv1a_32("*="): { return Opers::assign_product; } - case utility::fnv1a_32("+="): { return Opers::assign_sum; } - case utility::fnv1a_32("-="): { return Opers::assign_difference; } - case utility::fnv1a_32("&="): { return Opers::assign_bitwise_and; } - case utility::fnv1a_32("|="): { return Opers::assign_bitwise_or; } - case utility::fnv1a_32("<<="): { return Opers::assign_shift_left; } - case utility::fnv1a_32(">>="): { return Opers::assign_shift_right; } - case utility::fnv1a_32("%="): { return Opers::assign_remainder; } - case utility::fnv1a_32("^="): { return Opers::assign_bitwise_xor; } - case utility::fnv1a_32("<<"): { return Opers::shift_left; } - case utility::fnv1a_32(">>"): { return Opers::shift_right; } - case utility::fnv1a_32("%"): { return Opers::remainder; } - case utility::fnv1a_32("&"): { return Opers::bitwise_and; } - case utility::fnv1a_32("|"): { return Opers::bitwise_or; } - case utility::fnv1a_32("^"): { return Opers::bitwise_xor; } - case utility::fnv1a_32("~"): { return Opers::bitwise_complement; } - case utility::fnv1a_32("+"): { return t_is_unary ? Opers::unary_plus : Opers::sum; } - case utility::fnv1a_32("-"): { return t_is_unary ? Opers::unary_minus : Opers::difference; } - case utility::fnv1a_32("/"): { return Opers::quotient; } - case utility::fnv1a_32("*"): { return Opers::product; } - default: { return Opers::invalid; } + case utility::hash("=="): { + return Opers::equals; + } + case utility::hash("<"): { + return Opers::less_than; + } + case utility::hash(">"): { + return Opers::greater_than; + } + case utility::hash("<="): { + return Opers::less_than_equal; + } + case utility::hash(">="): { + return Opers::greater_than_equal; + } + case utility::hash("!="): { + return Opers::not_equal; + } + case utility::hash("="): { + return Opers::assign; + } + case utility::hash("++"): { + return Opers::pre_increment; + } + case utility::hash("--"): { + return Opers::pre_decrement; + } + case utility::hash("*="): { + return Opers::assign_product; + } + case utility::hash("+="): { + return Opers::assign_sum; + } + case utility::hash("-="): { + return Opers::assign_difference; + } + case utility::hash("&="): { + return Opers::assign_bitwise_and; + } + case utility::hash("|="): { + return Opers::assign_bitwise_or; + } + case utility::hash("<<="): { + return Opers::assign_shift_left; + } + case utility::hash(">>="): { + return Opers::assign_shift_right; + } + case utility::hash("%="): { + return Opers::assign_remainder; + } + case utility::hash("^="): { + return Opers::assign_bitwise_xor; + } + case utility::hash("<<"): { + return Opers::shift_left; + } + case utility::hash(">>"): { + return Opers::shift_right; + } + case utility::hash("%"): { + return Opers::remainder; + } + case utility::hash("&"): { + return Opers::bitwise_and; + } + case utility::hash("|"): { + return Opers::bitwise_or; + } + case utility::hash("^"): { + return Opers::bitwise_xor; + } + case utility::hash("~"): { + return Opers::bitwise_complement; + } + case utility::hash("+"): { + return t_is_unary ? Opers::unary_plus : Opers::sum; + } + case utility::hash("-"): { + return t_is_unary ? Opers::unary_minus : Opers::difference; + } + case utility::hash("/"): { + return Opers::quotient; + } + case utility::hash("*"): { + return Opers::product; + } + default: { + return Opers::invalid; + } } #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif - } - }; -} +} // namespace chaiscript #endif /* _CHAISCRIPT_ALGEBRAIC_HPP */ - diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 73e5f027..f4d51a19 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_COMMON_HPP_ #define CHAISCRIPT_COMMON_HPP_ @@ -23,134 +22,172 @@ #include "../dispatchkit/dispatchkit.hpp" #include "../dispatchkit/proxy_functions.hpp" #include "../dispatchkit/type_info.hpp" +#include namespace chaiscript { -struct AST_Node; -} // namespace chaiscript + struct AST_Node; +} // namespace chaiscript -namespace chaiscript -{ +namespace chaiscript { struct Name_Validator { - static bool is_reserved_word(const std::string &name) - { - static const std::set m_reserved_words - = {"def", "fun", "while", "for", "if", "else", "&&", "||", ",", "auto", - "return", "break", "true", "false", "class", "attr", "var", "global", "GLOBAL", "_", - "__LINE__", "__FILE__", "__FUNC__", "__CLASS__"}; - return m_reserved_words.count(name) > 0; + template + static bool is_reserved_word(const T &s) noexcept { + const static std::unordered_set + words{utility::hash("def"), utility::hash("fun"), utility::hash("while"), utility::hash("for"), utility::hash("if"), utility::hash("else"), utility::hash("&&"), utility::hash("||"), utility::hash(","), utility::hash("auto"), utility::hash("return"), utility::hash("break"), utility::hash("true"), utility::hash("false"), utility::hash("class"), utility::hash("attr"), utility::hash("var"), utility::hash("global"), utility::hash("GLOBAL"), utility::hash("_"), utility::hash("__LINE__"), utility::hash("__FILE__"), utility::hash("__FUNC__"), utility::hash("__CLASS__")}; + + return words.count(utility::hash(s)) == 1; } - static bool valid_object_name(const std::string &name) - { + template + static bool valid_object_name(const T &name) noexcept { return name.find("::") == std::string::npos && !is_reserved_word(name); } - static void validate_object_name(const std::string &name) - { + template + static void validate_object_name(const T &name) { if (is_reserved_word(name)) { - throw exception::reserved_word_error(name); + throw exception::reserved_word_error(std::string(name)); } if (name.find("::") != std::string::npos) { - throw exception::illegal_name_error(name); + throw exception::illegal_name_error(std::string(name)); } } }; /// Signature of module entry point that all binary loadable modules must implement. - typedef ModulePtr (*Create_Module_Func)(); - + using Create_Module_Func = ModulePtr (*)(); /// Types of AST nodes available to the parser and eval - enum class AST_Node_Type { 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 + enum class AST_Node_Type { + 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 }; - enum class Operator_Precidence { Ternary_Cond, Logical_Or, - Logical_And, Bitwise_Or, Bitwise_Xor, Bitwise_And, - Equality, Comparison, Shift, Addition, Multiplication, Prefix }; + enum class Operator_Precedence { + Ternary_Cond, + Logical_Or, + Logical_And, + Bitwise_Or, + Bitwise_Xor, + Bitwise_And, + Equality, + Comparison, + Shift, + Addition, + Multiplication, + Prefix + }; - namespace - { + namespace { /// Helper lookup to get the name of each node type - inline const char *ast_node_type_to_string(AST_Node_Type ast_node_type) { - static 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"}; + 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"}; return ast_node_types[static_cast(ast_node_type)]; } - } + } // namespace /// \brief Convenience type for file positions struct File_Position { - int line; - int column; + int line = 0; + int column = 0; - File_Position(int t_file_line, int t_file_column) - : line(t_file_line), column(t_file_column) { } + constexpr File_Position(int t_file_line, int t_file_column) noexcept + : line(t_file_line) + , column(t_file_column) { + } - File_Position() : line(0), column(0) { } + constexpr File_Position() noexcept = default; }; struct Parse_Location { - Parse_Location(std::string t_fname="", const int t_start_line=0, const int t_start_col=0, - const int t_end_line=0, const int t_end_col=0) - : start(t_start_line, t_start_col), - end(t_end_line, t_end_col), - filename(std::make_shared(std::move(t_fname))) - { + Parse_Location(std::string t_fname = "", const int t_start_line = 0, const int t_start_col = 0, const int t_end_line = 0, const int t_end_col = 0) + : start(t_start_line, t_start_col) + , end(t_end_line, t_end_col) + , filename(std::make_shared(std::move(t_fname))) { } - Parse_Location(std::shared_ptr t_fname, const int t_start_line=0, const int t_start_col=0, - const int t_end_line=0, const int t_end_col=0) - : start(t_start_line, t_start_col), - end(t_end_line, t_end_col), - filename(std::move(t_fname)) - { + Parse_Location(std::shared_ptr t_fname, + const int t_start_line = 0, + const int t_start_col = 0, + const int t_end_line = 0, + const int t_end_col = 0) + : start(t_start_line, t_start_col) + , end(t_end_line, t_end_col) + , filename(std::move(t_fname)) { } - - File_Position start; File_Position end; std::shared_ptr filename; }; - /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree - typedef std::unique_ptr AST_NodePtr; - typedef std::unique_ptr AST_NodePtr_Const; + using AST_NodePtr = std::unique_ptr; + using AST_NodePtr_Const = std::unique_ptr; struct AST_Node_Trace; - /// \brief Classes which may be thrown during error cases when ChaiScript is executing. - namespace exception - { + namespace exception { /// \brief Thrown if an error occurs while attempting to load a binary module - struct load_module_error : std::runtime_error - { - explicit load_module_error(const std::string &t_reason) noexcept - : std::runtime_error(t_reason) - { + struct load_module_error : std::runtime_error { + explicit load_module_error(const std::string &t_reason) + : std::runtime_error(t_reason) { } load_module_error(const std::string &t_name, const std::vector &t_errors) - : std::runtime_error(format_error(t_name, t_errors)) - { + : std::runtime_error(format_error(t_name, t_errors)) { } load_module_error(const load_module_error &) = default; ~load_module_error() noexcept override = default; - static std::string format_error(const std::string &t_name, const std::vector &t_errors) - { + static std::string format_error(const std::string &t_name, const std::vector &t_errors) { std::stringstream ss; ss << "Error loading module '" << t_name << "'\n" << " The following locations were searched:\n"; @@ -163,7 +200,6 @@ namespace chaiscript } }; - /// Errors generated during parsing or evaluation struct eval_error : std::runtime_error { std::string reason; @@ -172,48 +208,55 @@ namespace chaiscript std::string detail; std::vector call_stack; - eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, - const std::vector &t_parameters, const std::vector &t_functions, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) noexcept : - std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)), - reason(t_why), start_position(t_where), filename(t_fname), detail(format_detail(t_functions, t_dot_notation, t_ss)) - {} + eval_error(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname, + const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) noexcept + : std::runtime_error(format(t_why, t_where, t_fname, t_parameters, t_dot_notation, t_ss)) + , reason(t_why) + , start_position(t_where) + , filename(t_fname) + , detail(format_detail(t_functions, t_dot_notation, t_ss)) { + } - eval_error(const std::string &t_why, - const std::vector &t_parameters, const std::vector &t_functions, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) noexcept : - std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)), - reason(t_why), detail(format_detail(t_functions, t_dot_notation, t_ss)) - {} + eval_error(const std::string &t_why, + const std::vector &t_parameters, + const std::vector &t_functions, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) noexcept + : std::runtime_error(format(t_why, t_parameters, t_dot_notation, t_ss)) + , reason(t_why) + , detail(format_detail(t_functions, t_dot_notation, t_ss)) { + } - - eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) noexcept : - std::runtime_error(format(t_why, t_where, t_fname)), - reason(t_why), start_position(t_where), filename(t_fname) - {} + eval_error(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) noexcept + : std::runtime_error(format(t_why, t_where, t_fname)) + , reason(t_why) + , start_position(t_where) + , filename(t_fname) { + } explicit eval_error(const std::string &t_why) noexcept - : std::runtime_error("Error: \"" + t_why + "\" "), - reason(t_why) - {} + : std::runtime_error("Error: \"" + t_why + "\" ") + , reason(t_why) { + } eval_error(const eval_error &) = default; - std::string pretty_print() const - { + std::string pretty_print() const { std::ostringstream ss; ss << what(); if (!call_stack.empty()) { ss << "during evaluation at (" << fname(call_stack[0]) << " " << startpos(call_stack[0]) << ")\n"; - ss << '\n' << detail << '\n'; + ss << '\n' + << detail << '\n'; ss << " " << fname(call_stack[0]) << " (" << startpos(call_stack[0]) << ") '" << pretty(call_stack[0]) << "'"; for (size_t j = 1; j < call_stack.size(); ++j) { - if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block - && id(call_stack[j]) != chaiscript::AST_Node_Type::File) - { + if (id(call_stack[j]) != chaiscript::AST_Node_Type::Block && id(call_stack[j]) != chaiscript::AST_Node_Type::File) { ss << '\n'; ss << " from " << fname(call_stack[j]) << " (" << startpos(call_stack[j]) << ") '" << pretty(call_stack[j]) << "'"; } @@ -226,52 +269,39 @@ namespace chaiscript ~eval_error() noexcept override = default; private: - template - static AST_Node_Type id(const T& t) - { - return t.identifier; - } - - template - static std::string pretty(const T& t) - { - return t.pretty_print(); - } - - template - static const std::string &fname(const T& t) - { - return t.filename(); - } - - template - static std::string startpos(const T& t) - { - std::ostringstream oss; - oss << t.start().line << ", " << t.start().column; - return oss.str(); - } - - static std::string format_why(const std::string &t_why) - { - return "Error: \"" + t_why + "\""; + static AST_Node_Type id(const T &t) noexcept { + return t.identifier; } - static std::string format_types(const Const_Proxy_Function &t_func, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) - { + template + static std::string pretty(const T &t) { + return t.pretty_print(); + } + + template + static const std::string &fname(const T &t) noexcept { + return t.filename(); + } + + template + static std::string startpos(const T &t) { + std::ostringstream oss; + oss << t.start().line << ", " << t.start().column; + return oss.str(); + } + + static std::string format_why(const std::string &t_why) { return "Error: \"" + t_why + "\""; } + + static std::string format_types(const Const_Proxy_Function &t_func, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { assert(t_func); int arity = t_func->get_arity(); std::vector types = t_func->get_param_types(); std::string retval; - if (arity == -1) - { + if (arity == -1) { retval = "(...)"; - if (t_dot_notation) - { + if (t_dot_notation) { retval = "(Object)." + retval; } } else if (types.size() <= 1) { @@ -282,18 +312,13 @@ namespace chaiscript std::string paramstr; - for (size_t index = 1; - index != types.size(); - ++index) - { - paramstr += (types[index].is_const()?"const ":""); + for (size_t index = 1; index != types.size(); ++index) { + paramstr += (types[index].is_const() ? "const " : ""); paramstr += t_ss.get_type_name(types[index]); - if (index == 1 && t_dot_notation) - { + if (index == 1 && t_dot_notation) { paramstr += ").("; - if (types.size() == 2) - { + if (types.size() == 2) { paramstr += ", "; } } else { @@ -307,19 +332,15 @@ namespace chaiscript retval = ss.str(); } + std::shared_ptr dynfun + = std::dynamic_pointer_cast(t_func); - std::shared_ptr dynfun - = std::dynamic_pointer_cast(t_func); - - if (dynfun && dynfun->has_parse_tree()) - { + if (dynfun && dynfun->has_parse_tree()) { Proxy_Function f = dynfun->get_guard(); - if (f) - { + if (f) { auto dynfunguard = std::dynamic_pointer_cast(f); - if (dynfunguard && dynfunguard->has_parse_tree()) - { + if (dynfunguard && dynfunguard->has_parse_tree()) { retval += " : " + format_guard(dynfunguard->get_parse_tree()); } } @@ -331,65 +352,50 @@ namespace chaiscript } template - static std::string format_guard(const T &t) - { - return t.pretty_print(); - } + static std::string format_guard(const T &t) { + return t.pretty_print(); + } template - static std::string format_location(const T &t) - { - std::ostringstream oss; - oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; - return oss.str(); - } + static std::string format_location(const T &t) { + std::ostringstream oss; + oss << "(" << t.filename() << " " << t.start().line << ", " << t.start().column << ")"; + return oss.str(); + } static std::string format_detail(const std::vector &t_functions, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) - { + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) { std::stringstream ss; - if (t_functions.size() == 1) - { + if (t_functions.size() == 1) { assert(t_functions[0]); ss << " Expected: " << format_types(t_functions[0], t_dot_notation, t_ss) << '\n'; } else { ss << " " << t_functions.size() << " overloads available:\n"; - for (const auto & t_function : t_functions) - { + for (const auto &t_function : t_functions) { ss << " " << format_types((t_function), t_dot_notation, t_ss) << '\n'; } - } return ss.str(); - } - static std::string format_parameters(const std::vector &t_parameters, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) - { + static std::string + format_parameters(const std::vector &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) { std::stringstream ss; ss << "("; - if (!t_parameters.empty()) - { + if (!t_parameters.empty()) { std::string paramstr; - for (auto itr = t_parameters.begin(); - itr != t_parameters.end(); - ++itr) - { - paramstr += (itr->is_const()?"const ":""); + for (auto itr = t_parameters.begin(); itr != t_parameters.end(); ++itr) { + paramstr += (itr->is_const() ? "const " : ""); paramstr += t_ss.type_name(*itr); - if (itr == t_parameters.begin() && t_dot_notation) - { + if (itr == t_parameters.begin() && t_dot_notation) { paramstr += ").("; - if (t_parameters.size() == 1) - { + if (t_parameters.size() == 1) { paramstr += ", "; } } else { @@ -404,12 +410,10 @@ namespace chaiscript return ss.str(); } - static std::string format_filename(const std::string &t_fname) - { + static std::string format_filename(const std::string &t_fname) { std::stringstream ss; - if (t_fname != "__EVAL__") - { + if (t_fname != "__EVAL__") { ss << "in '" << t_fname << "' "; } else { ss << "during evaluation "; @@ -418,16 +422,18 @@ namespace chaiscript return ss.str(); } - static std::string format_location(const File_Position &t_where) - { + static std::string format_location(const File_Position &t_where) { std::stringstream ss; ss << "at (" << t_where.line << ", " << t_where.column << ")"; return ss.str(); } - static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname, - const std::vector &t_parameters, bool t_dot_notation, const chaiscript::detail::Dispatch_Engine &t_ss) - { + static std::string format(const std::string &t_why, + const File_Position &t_where, + const std::string &t_fname, + const std::vector &t_parameters, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) { std::stringstream ss; ss << format_why(t_why); @@ -444,11 +450,10 @@ namespace chaiscript return ss.str(); } - static std::string format(const std::string &t_why, - const std::vector &t_parameters, - bool t_dot_notation, - const chaiscript::detail::Dispatch_Engine &t_ss) - { + static std::string format(const std::string &t_why, + const std::vector &t_parameters, + bool t_dot_notation, + const chaiscript::detail::Dispatch_Engine &t_ss) { std::stringstream ss; ss << format_why(t_why); @@ -460,8 +465,7 @@ namespace chaiscript return ss.str(); } - static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) - { + static std::string format(const std::string &t_why, const File_Position &t_where, const std::string &t_fname) { std::stringstream ss; ss << format_why(t_why); @@ -476,13 +480,12 @@ namespace chaiscript } }; - /// Errors generated when loading a file struct file_not_found_error : std::runtime_error { - explicit file_not_found_error(const std::string &t_filename) noexcept - : std::runtime_error("File Not Found: " + t_filename), - filename(t_filename) - { } + explicit file_not_found_error(const std::string &t_filename) + : std::runtime_error("File Not Found: " + t_filename) + , filename(t_filename) { + } file_not_found_error(const file_not_found_error &) = default; ~file_not_found_error() noexcept override = default; @@ -490,265 +493,204 @@ namespace chaiscript std::string filename; }; - } + } // namespace exception - /// \brief Struct that doubles as both a parser ast_node and an AST node. struct AST_Node { - public: - const AST_Node_Type identifier; - const std::string text; - Parse_Location location; - - const std::string &filename() const { - return *location.filename; - } - - const File_Position &start() const { - return location.start; - } - - const File_Position &end() const { - return location.end; - } - - std::string pretty_print() const - { - std::ostringstream oss; - - oss << text; - - for (auto & elem : get_children()) { - oss << elem.get().pretty_print() << ' '; - } - - return oss.str(); - } - - virtual std::vector> get_children() const = 0; - virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0; - - - /// Prints the contents of an AST node, including its children, recursively - std::string to_string(const std::string &t_prepend = "") const { - std::ostringstream oss; - - oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " - << this->text << " : " << this->location.start.line << ", " << this->location.start.column << '\n'; - - for (auto & elem : get_children()) { - oss << elem.get().to_string(t_prepend + " "); - } - return oss.str(); - } - - - static bool get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) { - try { - return t_ss->boxed_cast(t_bv); - } - catch (const exception::bad_boxed_cast &) { - throw exception::eval_error("Condition not boolean"); - } - } - - - virtual ~AST_Node() = default; - AST_Node(AST_Node &&) = default; - AST_Node &operator=(AST_Node &&) = default; - AST_Node(const AST_Node &) = delete; - AST_Node& operator=(const AST_Node &) = delete; - - - protected: - AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc) - : identifier(t_id), text(std::move(t_ast_node_text)), - location(std::move(t_loc)) - { - } - - - }; - - struct AST_Node_Trace - { + public: const AST_Node_Type identifier; const std::string text; Parse_Location location; - const std::string &filename() const { - return *location.filename; - } + const std::string &filename() const noexcept { return *location.filename; } - const File_Position &start() const { - return location.start; - } + const File_Position &start() const noexcept { return location.start; } - const File_Position &end() const { - return location.end; - } + const File_Position &end() const noexcept { return location.end; } - std::string pretty_print() const - { + std::string pretty_print() const { std::ostringstream oss; oss << text; - for (const auto & elem : children) { + for (auto &elem : get_children()) { + oss << elem.get().pretty_print() << ' '; + } + + return oss.str(); + } + + virtual std::vector> get_children() const = 0; + virtual Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const = 0; + + /// Prints the contents of an AST node, including its children, recursively + std::string to_string(const std::string &t_prepend = "") const { + std::ostringstream oss; + + oss << t_prepend << "(" << ast_node_type_to_string(this->identifier) << ") " << this->text << " : " << this->location.start.line + << ", " << this->location.start.column << '\n'; + + for (auto &elem : get_children()) { + oss << elem.get().to_string(t_prepend + " "); + } + return oss.str(); + } + + static bool get_bool_condition(const Boxed_Value &t_bv, const chaiscript::detail::Dispatch_State &t_ss) { + try { + return t_ss->boxed_cast(t_bv); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Condition not boolean"); + } + } + + virtual ~AST_Node() noexcept = default; + AST_Node(AST_Node &&) = default; + AST_Node &operator=(AST_Node &&) = delete; + AST_Node(const AST_Node &) = delete; + AST_Node &operator=(const AST_Node &) = delete; + + protected: + AST_Node(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc) + : identifier(t_id) + , text(std::move(t_ast_node_text)) + , location(std::move(t_loc)) { + } + }; + + struct AST_Node_Trace { + const AST_Node_Type identifier; + const std::string text; + Parse_Location location; + + const std::string &filename() const noexcept { return *location.filename; } + + const File_Position &start() const noexcept { return location.start; } + + const File_Position &end() const noexcept { return location.end; } + + std::string pretty_print() const { + std::ostringstream oss; + + oss << text; + + for (const auto &elem : children) { oss << elem.pretty_print() << ' '; } return oss.str(); } - std::vector get_children(const AST_Node &node) - { + std::vector get_children(const AST_Node &node) { const auto node_children = node.get_children(); return std::vector(node_children.begin(), node_children.end()); } AST_Node_Trace(const AST_Node &node) - : identifier(node.identifier), text(node.text), - location(node.location), children(get_children(node)) - { + : identifier(node.identifier) + , text(node.text) + , location(node.location) + , children(get_children(node)) { } - std::vector children; - }; namespace parser { - class ChaiScript_Parser_Base - { - public: - virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0; - virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0; - virtual void *get_tracer_ptr() = 0; - virtual ~ChaiScript_Parser_Base() = default; - ChaiScript_Parser_Base() = default; - ChaiScript_Parser_Base(ChaiScript_Parser_Base &&) = default; - ChaiScript_Parser_Base &operator=(ChaiScript_Parser_Base &&) = delete; - ChaiScript_Parser_Base &operator=(const ChaiScript_Parser_Base &&) = delete; + class ChaiScript_Parser_Base { + public: + virtual AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) = 0; + virtual void debug_print(const AST_Node &t, std::string prepend = "") const = 0; + virtual void *get_tracer_ptr() = 0; + virtual ~ChaiScript_Parser_Base() = default; + ChaiScript_Parser_Base() = default; + ChaiScript_Parser_Base(ChaiScript_Parser_Base &&) = default; + ChaiScript_Parser_Base &operator=(ChaiScript_Parser_Base &&) = delete; + ChaiScript_Parser_Base &operator=(const ChaiScript_Parser_Base &&) = delete; - template - T &get_tracer() - { - // to do type check this somehow? - return *static_cast(get_tracer_ptr()); - } + template + T &get_tracer() noexcept { + // to do type check this somehow? + return *static_cast(get_tracer_ptr()); + } - protected: - ChaiScript_Parser_Base(const ChaiScript_Parser_Base &) = default; + protected: + ChaiScript_Parser_Base(const ChaiScript_Parser_Base &) = default; }; - } + } // namespace parser - namespace eval - { - namespace detail - { + namespace eval { + namespace detail { /// Special type for returned values struct Return_Value { Boxed_Value retval; }; - /// Special type indicating a call to 'break' struct Break_Loop { - Break_Loop() = default; }; - /// Special type indicating a call to 'continue' struct Continue_Loop { - Continue_Loop() = default; }; - /// Creates a new scope then pops it on destruction - struct Scope_Push_Pop - { + struct Scope_Push_Pop { Scope_Push_Pop(Scope_Push_Pop &&) = default; - Scope_Push_Pop& operator=(Scope_Push_Pop &&) = default; + Scope_Push_Pop &operator=(Scope_Push_Pop &&) = delete; Scope_Push_Pop(const Scope_Push_Pop &) = delete; - Scope_Push_Pop& operator=(const Scope_Push_Pop &) = delete; + Scope_Push_Pop &operator=(const Scope_Push_Pop &) = delete; explicit Scope_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) - : m_ds(t_ds) - { + : m_ds(t_ds) { m_ds->new_scope(m_ds.stack_holder()); } - ~Scope_Push_Pop() - { - m_ds->pop_scope(m_ds.stack_holder()); - } + ~Scope_Push_Pop() { m_ds->pop_scope(m_ds.stack_holder()); } - - private: - const chaiscript::detail::Dispatch_State &m_ds; + private: + const chaiscript::detail::Dispatch_State &m_ds; }; /// Creates a new function call and pops it on destruction - struct Function_Push_Pop - { + struct Function_Push_Pop { Function_Push_Pop(Function_Push_Pop &&) = default; - Function_Push_Pop& operator=(Function_Push_Pop &&) = default; + Function_Push_Pop &operator=(Function_Push_Pop &&) = delete; Function_Push_Pop(const Function_Push_Pop &) = delete; - Function_Push_Pop& operator=(const Function_Push_Pop &) = delete; + Function_Push_Pop &operator=(const Function_Push_Pop &) = delete; explicit Function_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) - : m_ds(t_ds) - { + : m_ds(t_ds) { m_ds->new_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } - ~Function_Push_Pop() - { - m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); - } + ~Function_Push_Pop() { m_ds->pop_function_call(m_ds.stack_holder(), m_ds.conversion_saves()); } - void save_params(const std::vector &t_params) - { - m_ds->save_function_params(t_params); - } + void save_params(const Function_Params &t_params) { m_ds->save_function_params(t_params); } - void save_params(std::initializer_list t_params) - { - m_ds->save_function_params(t_params); - } - - - private: - const chaiscript::detail::Dispatch_State &m_ds; + private: + const chaiscript::detail::Dispatch_State &m_ds; }; /// Creates a new scope then pops it on destruction - struct Stack_Push_Pop - { + struct Stack_Push_Pop { Stack_Push_Pop(Stack_Push_Pop &&) = default; - Stack_Push_Pop& operator=(Stack_Push_Pop &&) = default; + Stack_Push_Pop &operator=(Stack_Push_Pop &&) = delete; Stack_Push_Pop(const Stack_Push_Pop &) = delete; - Stack_Push_Pop& operator=(const Stack_Push_Pop &) = delete; + Stack_Push_Pop &operator=(const Stack_Push_Pop &) = delete; explicit Stack_Push_Pop(const chaiscript::detail::Dispatch_State &t_ds) - : m_ds(t_ds) - { + : m_ds(t_ds) { m_ds->new_stack(m_ds.stack_holder()); } - ~Stack_Push_Pop() - { - m_ds->pop_stack(m_ds.stack_holder()); - } + ~Stack_Push_Pop() { m_ds->pop_stack(m_ds.stack_holder()); } - - private: - const chaiscript::detail::Dispatch_State &m_ds; + private: + const chaiscript::detail::Dispatch_State &m_ds; }; - } - } -} + } // namespace detail + } // namespace eval +} // namespace chaiscript #endif /* _CHAISCRIPT_COMMON_HPP */ - diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 3e6d0d84..9950ec50 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -1,17 +1,17 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_ENGINE_HPP_ #define CHAISCRIPT_ENGINE_HPP_ #include +#include #include #include #include @@ -21,15 +21,15 @@ #include #include #include -#include #include "../chaiscript_defines.hpp" #include "../chaiscript_threading.hpp" #include "../dispatchkit/boxed_cast_helper.hpp" #include "../dispatchkit/boxed_value.hpp" #include "../dispatchkit/dispatchkit.hpp" -#include "../dispatchkit/type_conversions.hpp" #include "../dispatchkit/proxy_functions.hpp" +#include "../dispatchkit/register_function.hpp" +#include "../dispatchkit/type_conversions.hpp" #include "chaiscript_common.hpp" #if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__) @@ -50,23 +50,18 @@ #include "chaiscript_unknown.hpp" #endif - #include "../dispatchkit/exception_specification.hpp" -namespace chaiscript -{ - /// Namespace alias to provide cleaner and more explicit syntax to users. - using Namespace = dispatch::Dynamic_Object; +namespace chaiscript { + /// Namespace alias to provide cleaner and more explicit syntax to users. + using Namespace = dispatch::Dynamic_Object; - namespace detail - { - typedef std::shared_ptr Loadable_Module_Ptr; + namespace detail { + using Loadable_Module_Ptr = std::shared_ptr; } - /// \brief The main object that the ChaiScript user will use. class ChaiScript_Basic { - mutable chaiscript::detail::threading::shared_mutex m_mutex; mutable chaiscript::detail::threading::recursive_mutex m_use_mutex; @@ -81,26 +76,21 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine m_engine; - std::map> m_namespace_generators; + std::map> m_namespace_generators; /// Evaluates the given string in by parsing it and running the results through the evaluator - Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) - { + Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false) { try { const auto p = m_parser->parse(t_input, t_filename); return p->eval(chaiscript::detail::Dispatch_State(m_engine)); - } - catch (chaiscript::eval::detail::Return_Value &rv) { + } catch (chaiscript::eval::detail::Return_Value &rv) { return rv.retval; } } - - /// Evaluates the given file and looks in the 'use' paths - const Boxed_Value internal_eval_file(const std::string &t_filename) { - for (const auto &path : m_use_paths) - { + Boxed_Value internal_eval_file(const std::string &t_filename) { + for (const auto &path : m_use_paths) { try { const auto appendedpath = path + t_filename; return do_eval(load_file(appendedpath), appendedpath, true); @@ -113,13 +103,10 @@ namespace chaiscript // failed to load by any name throw exception::file_not_found_error(t_filename); - } - - /// Evaluates the given string, used during eval() inside of a script - const Boxed_Value internal_eval(const std::string &t_e) { + Boxed_Value internal_eval(const std::string &t_e) { try { return do_eval(t_e, "__EVAL__", true); } catch (const exception::eval_error &t_ee) { @@ -128,107 +115,94 @@ namespace chaiscript } /// Returns the current evaluation m_engine - chaiscript::detail::Dispatch_Engine &get_eval_engine() { - return m_engine; - } + 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) { - if (t_lib) - { + if (t_lib) { add(t_lib); } - m_engine.add(fun([this](){ m_engine.dump_system(); }), "dump_system"); - m_engine.add(fun([this](const Boxed_Value &t_bv){ m_engine.dump_object(t_bv); }), "dump_object"); - m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type){ return m_engine.is_type(t_bv, t_type); }), "is_type"); - m_engine.add(fun([this](const Boxed_Value &t_bv){ return m_engine.type_name(t_bv); }), "type_name"); - m_engine.add(fun([this](const std::string &t_f){ return m_engine.function_exists(t_f); }), "function_exists"); - m_engine.add(fun([this](){ return m_engine.get_function_objects(); }), "get_functions"); - m_engine.add(fun([this](){ return m_engine.get_scripting_objects(); }), "get_objects"); + m_engine.add(fun([this]() { m_engine.dump_system(); }), "dump_system"); + m_engine.add(fun([this](const Boxed_Value &t_bv) { m_engine.dump_object(t_bv); }), "dump_object"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type) { return m_engine.is_type(t_bv, t_type); }), "is_type"); + m_engine.add(fun([this](const Boxed_Value &t_bv) { return m_engine.type_name(t_bv); }), "type_name"); + m_engine.add(fun([this](const std::string &t_f) { return m_engine.function_exists(t_f); }), "function_exists"); + m_engine.add(fun([this]() { return m_engine.get_function_objects(); }), "get_functions"); + m_engine.add(fun([this]() { return m_engine.get_scripting_objects(); }), "get_objects"); - m_engine.add( - dispatch::make_dynamic_proxy_function( - [this](const std::vector &t_params) { - return m_engine.call_exists(t_params); - }) - , "call_exists"); + m_engine.add(dispatch::make_dynamic_proxy_function([this](const Function_Params &t_params) { return m_engine.call_exists(t_params); }), + "call_exists"); + m_engine.add(fun([this](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) -> Boxed_Value { + Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves()); + return t_fun(Function_Params{t_params}, s); + }), + "call"); - m_engine.add(fun( - [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector &t_params) -> Boxed_Value { - Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves()); - return t_fun(t_params, s); - }), "call"); - - - m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name"); - - m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type"); - m_engine.add(fun([this](const std::string &t_type_name){ return m_engine.get_type(t_type_name, true); }), "type"); - - m_engine.add(fun( - [=](const Type_Info &t_from, const Type_Info &t_to, const std::function &t_func) { - m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func)); - } - ), "add_type_conversion"); + m_engine.add(fun([this](const Type_Info &t_ti) { return m_engine.get_type_name(t_ti); }), "name"); + m_engine.add(fun([this](const std::string &t_type_name, bool t_throw) { return m_engine.get_type(t_type_name, t_throw); }), "type"); + m_engine.add(fun([this](const std::string &t_type_name) { return m_engine.get_type(t_type_name, true); }), "type"); + m_engine.add(fun([this](const Type_Info &t_from, const Type_Info &t_to, const std::function &t_func) { + m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func)); + }), + "add_type_conversion"); if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == t_opts.end() - && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end()) - { - m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module"); - m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module"); + && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end()) { + m_engine.add(fun([this](const std::string &t_module, const std::string &t_file) { load_module(t_module, t_file); }), "load_module"); + m_engine.add(fun([this](const std::string &t_module) { return load_module(t_module); }), "load_module"); } if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == t_opts.end() - && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end()) - { - m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use"); - m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file"); + && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end()) { + m_engine.add(fun([this](const std::string &t_file) { return use(t_file); }), "use"); + m_engine.add(fun([this](const std::string &t_file) { return internal_eval_file(t_file); }), "eval_file"); } - m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval"); - m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval"); + m_engine.add(fun([this](const std::string &t_str) { return internal_eval(t_str); }), "eval"); + m_engine.add(fun([this](const AST_Node &t_ast) { return eval(t_ast); }), "eval"); - m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse"); - m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse"); + m_engine.add(fun([this](const std::string &t_str, const bool t_dump) { return parse(t_str, t_dump); }), "parse"); + m_engine.add(fun([this](const std::string &t_str) { return parse(t_str); }), "parse"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name) { add_global_const(t_bv, t_name); }), "add_global_const"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name) { add_global(t_bv, t_name); }), "add_global"); + m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name) { set_global(t_bv, t_name); }), "set_global"); - m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const"); - m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global"); - m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global"); - - m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& /*space*/) {}, t_namespace_name); import(t_namespace_name); }), "namespace"); - m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import"); + // why this unused parameter to Namespace? + m_engine.add(fun([this](const std::string &t_namespace_name) { + register_namespace([](Namespace & /*space*/) noexcept {}, t_namespace_name); + import(t_namespace_name); + }), + "namespace"); + m_engine.add(fun([this](const std::string &t_namespace_name) { import(t_namespace_name); }), "import"); } /// Skip BOM at the beginning of file static bool skip_bom(std::ifstream &infile) { - size_t bytes_needed = 3; - char buffer[3]; + size_t bytes_needed = 3; + char buffer[3]; - memset(buffer, '\0', bytes_needed); + memset(buffer, '\0', bytes_needed); - infile.read(buffer, static_cast(bytes_needed)); + infile.read(buffer, static_cast(bytes_needed)); - if ((buffer[0] == '\xef') - && (buffer[1] == '\xbb') - && (buffer[2] == '\xbf')) { + if ((buffer[0] == '\xef') && (buffer[1] == '\xbb') && (buffer[2] == '\xbf')) { + infile.seekg(3); + return true; + } - infile.seekg(3); - return true; - } + infile.seekg(0); - infile.seekg(0); - - return false; + return false; } /// Helper function for loading a file static std::string load_file(const std::string &t_filename) { - std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary ); + std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary); if (!infile.is_open()) { throw chaiscript::exception::file_not_found_error(t_filename); @@ -240,12 +214,11 @@ namespace chaiscript assert(size >= 0); if (skip_bom(infile)) { - size-=3; // decrement the BOM size from file size, otherwise we'll get parsing errors - assert(size >= 0); //and check if there's more text + size -= 3; // decrement the BOM size from file size, otherwise we'll get parsing errors + assert(size >= 0); // and check if there's more text } - if (size == std::streampos(0)) - { + if (size == std::streampos(0)) { return std::string(); } else { std::vector v(static_cast(size)); @@ -254,14 +227,15 @@ namespace chaiscript } } - std::vector ensure_minimum_path_vec(std::vector paths) - { - if (paths.empty()) { return {""}; } - else { return paths; } + std::vector ensure_minimum_path_vec(std::vector paths) { + if (paths.empty()) { + return {""}; + } else { + return paths; + } } public: - /// \brief Constructor for ChaiScript /// \param[in] t_lib Standard library to apply to this ChaiScript instance /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module @@ -271,42 +245,38 @@ namespace chaiscript std::vector t_module_paths = {}, std::vector t_use_paths = {}, const std::vector &t_opts = chaiscript::default_options()) - : 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)), - m_engine(*m_parser) - { + : 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)) + , m_engine(*m_parser) { #if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__) // If on Unix, add the path of the current executable to the module search path // as windows would do - union cast_union - { - Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string&); + union cast_union { + Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string &); void *out_ptr; }; - Dl_info rInfo; - memset( &rInfo, 0, sizeof(rInfo) ); + Dl_info rInfo; + memset(&rInfo, 0, sizeof(rInfo)); cast_union u; u.in_ptr = &ChaiScript_Basic::use; - if ( (dladdr(static_cast(u.out_ptr), &rInfo) != 0) && (rInfo.dli_fname != nullptr) ) { + if ((dladdr(static_cast(u.out_ptr), &rInfo) != 0) && (rInfo.dli_fname != nullptr)) { std::string dllpath(rInfo.dli_fname); const size_t lastslash = dllpath.rfind('/'); - if (lastslash != std::string::npos) - { + if (lastslash != std::string::npos) { dllpath.erase(lastslash); } // Let's see if this is a link that we should expand std::vector buf(2048); const auto pathlen = readlink(dllpath.c_str(), &buf.front(), buf.size()); - if (pathlen > 0 && static_cast(pathlen) < buf.size()) - { + if (pathlen > 0 && static_cast(pathlen) < buf.size()) { dllpath = std::string(&buf.front(), static_cast(pathlen)); } - m_module_paths.insert(m_module_paths.begin(), dllpath+"/"); + m_module_paths.insert(m_module_paths.begin(), dllpath + "/"); } #endif build_eval_system(t_lib, t_opts); @@ -314,48 +284,45 @@ namespace chaiscript #ifndef CHAISCRIPT_NO_DYNLOAD /// \brief Constructor for ChaiScript. - /// + /// /// This version of the ChaiScript constructor attempts to find the stdlib module to load /// at runtime generates an error if it cannot be found. /// /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file explicit ChaiScript_Basic(std::unique_ptr &&parser, - std::vector t_module_paths = {}, - std::vector t_use_paths = {}, - const std::vector &t_opts = chaiscript::default_options()) - : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts) - { + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) + : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts) { try { // attempt to load the stdlib load_module("chaiscript_stdlib-" + Build_Info::version()); } catch (const exception::load_module_error &t_err) { - std::cout << "An error occured while trying to load the chaiscript standard library.\n" - << "\n" - << "You must either provide a standard library, or compile it in.\n" - << "For an example of compiling the standard library in,\n" - << "see: https://gist.github.com/lefticus/9456197\n" - << "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n" - << "\n" - << "\n" + std::cout << "An error occurred while trying to load the chaiscript standard library.\n" + "\n" + "You must either provide a standard library, or compile it in.\n" + "For an example of compiling the standard library in,\n" + "see: https://gist.github.com/lefticus/9456197\n" + "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n" + "\n\n" << t_err.what(); throw; } } #else // CHAISCRIPT_NO_DYNLOAD -explicit ChaiScript_Basic(std::unique_ptr &&parser, - std::vector t_module_paths = {}, - std::vector t_use_paths = {}, - const std::vector &t_opts = chaiscript::default_options()) = delete; + explicit ChaiScript_Basic(std::unique_ptr &&parser, + std::vector t_module_paths = {}, + std::vector t_use_paths = {}, + const std::vector &t_opts = chaiscript::default_options()) + = delete; #endif - parser::ChaiScript_Parser_Base &get_parser() - { + parser::ChaiScript_Parser_Base &get_parser() noexcept { return *m_parser; } - const Boxed_Value eval(const AST_Node &t_ast) - { + const Boxed_Value eval(const AST_Node &t_ast) { try { return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine)); } catch (const exception::eval_error &t_ee) { @@ -363,8 +330,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars } } - AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false) - { + AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false) { auto ast = m_parser->parse(t_input, "PARSE"); if (t_debug_print) { m_parser->debug_print(*ast); @@ -372,38 +338,28 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars return ast; } - - std::string get_type_name(const Type_Info &ti) const - { - return m_engine.get_type_name(ti); - } + std::string get_type_name(const Type_Info &ti) const { return m_engine.get_type_name(ti); } template - std::string get_type_name() const - { + std::string get_type_name() const { return get_type_name(user_type()); } - - /// \brief Loads and parses a file. If the file is already, it is not reloaded - /// The use paths specified at ChaiScript construction time are searched for the - /// requested file. + /// \brief Loads and parses a file. If the file is already open, it is not + /// reloaded. The use paths specified at ChaiScript construction time are + /// searched for the requested file. /// /// \param[in] t_filename Filename to load and evaluate - Boxed_Value use(const std::string &t_filename) - { - for (const auto &path : m_use_paths) - { + Boxed_Value use(const std::string &t_filename) { + for (const auto &path : m_use_paths) { const auto appendedpath = path + t_filename; try { - chaiscript::detail::threading::unique_lock l(m_use_mutex); chaiscript::detail::threading::unique_lock l2(m_mutex); Boxed_Value retval; - if (m_used_files.count(appendedpath) == 0) - { + if (m_used_files.count(appendedpath) == 0) { l2.unlock(); retval = eval_file(appendedpath); l2.lock(); @@ -429,8 +385,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \param[in] t_name Name of the value to add /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object /// \sa Boxed_Value::is_const - ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) - { + ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name) { Name_Validator::validate_object_name(t_name); m_engine.add_global_const(t_bv, t_name); return *this; @@ -441,15 +396,13 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \param[in] t_name Name of the value to add /// \warning The user is responsible for making sure the object is thread-safe if necessary /// ChaiScript is thread-safe but provides no threading locking mechanism to the script - ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name) - { + ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name) { Name_Validator::validate_object_name(t_name); m_engine.add_global(t_bv, t_name); return *this; } - ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name) - { + ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name) { Name_Validator::validate_object_name(t_name); m_engine.set_global(t_bv, t_name); return *this; @@ -460,8 +413,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// are left out due to performance considerations involved in tracking the state /// \sa ChaiScript::get_state /// \sa ChaiScript::set_state - struct State - { + struct State { std::set used_files; chaiscript::detail::Dispatch_Engine::State engine_state; std::set active_loaded_modules; @@ -480,8 +432,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// chaiscript::ChaiScript chai; /// chaiscript::ChaiScript::State s = chai.get_state(); // represents bootstrapped initial state /// \endcode - State get_state() const - { + State get_state() const { chaiscript::detail::threading::lock_guard l(m_use_mutex); chaiscript::detail::threading::shared_lock l2(m_mutex); @@ -506,8 +457,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// chai.add(chaiscript::fun(&somefunction), "somefunction"); /// chai.set_state(s); // restore initial state, which does not have the recently added "somefunction" /// \endcode - void set_state(const State &t_state) - { + void set_state(const State &t_state) { chaiscript::detail::threading::lock_guard l(m_use_mutex); chaiscript::detail::threading::shared_lock l2(m_mutex); @@ -517,26 +467,20 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars } /// \returns All values in the local thread state, added through the add() function - std::map get_locals() const - { - return m_engine.get_locals(); - } + std::map get_locals() const { return m_engine.get_locals(); } /// \brief Sets all of the locals for the current thread state. /// /// \param[in] t_locals The map set of variables to replace the current state with /// /// Any existing locals are removed and the given set of variables is added - void set_locals(const std::map &t_locals) - { - m_engine.set_locals(t_locals); - } + void set_locals(const std::map &t_locals) { m_engine.set_locals(t_locals); } /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state. /// \param[in] t_t Item to add /// \param[in] t_name Name of item to add /// \returns Reference to current ChaiScript object - /// + /// /// \b Examples: /// \code /// chaiscript::ChaiScript chai; @@ -548,8 +492,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// /// \sa \ref adding_items template - ChaiScript_Basic &add(const T &t_t, const std::string &t_name) - { + ChaiScript_Basic &add(const T &t_t, const std::string &t_name) { Name_Validator::validate_object_name(t_name); m_engine.add(t_t, t_name); return *this; @@ -557,15 +500,14 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \brief Add a new conversion for upcasting to a base class /// \sa chaiscript::base_class - /// \param[in] d Base class / parent class + /// \param[in] d Base class / parent class /// /// \b Example: /// \code /// chaiscript::ChaiScript chai; /// chai.add(chaiscript::base_class()); /// \endcode - ChaiScript_Basic &add(const Type_Conversion &d) - { + ChaiScript_Basic &add(const Type_Conversion &d) { m_engine.add(d); return *this; } @@ -573,8 +515,7 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \brief Adds all elements of a module to ChaiScript runtime /// \param[in] t_p The module to add. /// \sa chaiscript::Module - ChaiScript_Basic &add(const ModulePtr &t_p) - { + ChaiScript_Basic &add(const ModulePtr &t_p) { t_p->apply(*this, this->get_eval_engine()); return *this; } @@ -587,21 +528,18 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// and with standard prefixes and postfixes: ("lib"|"")\(".dll"|".so"|".bundle"|""). /// /// Once the file is located, the system looks for the symbol "create_chaiscript_module_\". - /// If no file can be found matching the search criteria and containing the appropriate entry point + /// If no file can be found matching the search criteria and containing the appropriate entry point /// (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(const std::string &t_module_name) { #ifdef CHAISCRIPT_NO_DYNLOAD - (void)t_module_name; // -Wunused-parameter throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); #else std::vector errors; std::string version_stripped_name = t_module_name; size_t version_pos = version_stripped_name.find("-" + Build_Info::version()); - if (version_pos != std::string::npos) - { + if (version_pos != std::string::npos) { version_stripped_name.erase(version_pos); } @@ -609,12 +547,9 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars std::vector postfixes{".dll", ".so", ".bundle", ""}; - for (auto & elem : m_module_paths) - { - for (auto & prefix : prefixes) - { - for (auto & postfix : postfixes) - { + for (auto &elem : m_module_paths) { + for (auto &prefix : prefixes) { + for (auto &postfix : postfixes) { try { const auto name = elem + prefix + t_module_name + postfix; // std::cerr << "trying location: " << name << '\n'; @@ -640,12 +575,10 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \param[in] t_filename Ignore normal filename search process and use specific filename /// /// \sa ChaiScript::load_module(const std::string &t_module_name) - void load_module(const std::string &t_module_name, const std::string &t_filename) - { + void load_module(const std::string &t_module_name, const std::string &t_filename) { chaiscript::detail::threading::lock_guard l(m_use_mutex); - if (m_loaded_modules.count(t_module_name) == 0) - { + if (m_loaded_modules.count(t_module_name) == 0) { detail::Loadable_Module_Ptr lm(new detail::Loadable_Module(t_module_name, t_filename)); m_loaded_modules[t_module_name] = lm; m_active_loaded_modules.insert(t_module_name); @@ -653,20 +586,18 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars } else if (m_active_loaded_modules.count(t_module_name) == 0) { m_active_loaded_modules.insert(t_module_name); add(m_loaded_modules[t_module_name]->m_moduleptr); - } + } } - /// \brief Evaluates a string. Equivalent to ChaiScript::eval. /// /// \param[in] t_script Script to execute /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions /// /// \return result of the script execution - /// + /// /// \throw chaiscript::exception::eval_error In the case that evaluation fails. - Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler()) - { + Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler()) { return eval(t_script, t_handler); } @@ -679,23 +610,20 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// in special cases where you are loading a file internally instead of using eval_file /// /// \return result of the script execution - /// + /// /// \throw chaiscript::exception::eval_error In the case that evaluation fails. /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted /// to the requested type. template - T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__") - { + T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename = "__EVAL__") { return m_engine.boxed_cast(eval(t_input, t_handler, t_filename)); } /// \brief casts an object while applying any Dynamic_Conversion available template - decltype(auto) boxed_cast(const Boxed_Value &bv) const - { - return(m_engine.boxed_cast(bv)); - } - + decltype(auto) boxed_cast(const Boxed_Value &bv) const { + return (m_engine.boxed_cast(bv)); + } /// \brief Evaluates a string. /// @@ -705,10 +633,10 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// in special cases where you are loading a file internally instead of using eval_file /// /// \return result of the script execution - /// + /// /// \throw exception::eval_error In the case that evaluation fails. - Boxed_Value eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__") - { + Boxed_Value + eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename = "__EVAL__") { try { return do_eval(t_input, t_filename); } catch (Boxed_Value &bv) { @@ -744,39 +672,35 @@ explicit ChaiScript_Basic(std::unique_ptr &&pars /// \brief Imports a namespace object into the global scope of this ChaiScript instance. /// \param[in] t_namespace_name Name of the namespace to import. /// \throw std::runtime_error In the case that the namespace name was never registered. - void import(const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); + void import(const std::string &t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); - if (m_engine.get_scripting_objects().count(t_namespace_name)) { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined"); - } - else if (m_namespace_generators.count(t_namespace_name)) { - m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name); - } - else { - throw std::runtime_error("No registered namespace: " + t_namespace_name); - } + if (m_engine.get_scripting_objects().count(t_namespace_name)) { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined"); + } else if (m_namespace_generators.count(t_namespace_name)) { + m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name); + } else { + throw std::runtime_error("No registered namespace: " + t_namespace_name); + } } - /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used. - /// \param[in] t_namespace_generator Namespace generator function. - /// \param[in] t_namespace_name Name of the Namespace function being registered. - /// \throw std::runtime_error In the case that the namespace name was already registered. - void register_namespace(const std::function& t_namespace_generator, const std::string& t_namespace_name) - { - chaiscript::detail::threading::unique_lock l(m_use_mutex); + /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never + /// used. \param[in] t_namespace_generator Namespace generator function. \param[in] t_namespace_name Name of the Namespace function + /// being registered. \throw std::runtime_error In the case that the namespace name was already registered. + void register_namespace(const std::function &t_namespace_generator, const std::string &t_namespace_name) { + chaiscript::detail::threading::unique_lock l(m_use_mutex); - if (!m_namespace_generators.count(t_namespace_name)) { - // contain the namespace object memory within the m_namespace_generators map - m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; })); - } - else { - throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); - } + if (!m_namespace_generators.count(t_namespace_name)) { + // contain the namespace object memory within the m_namespace_generators map + m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace & { + t_namespace_generator(space); + return space; + })); + } else { + throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered."); + } } }; -} +} // namespace chaiscript #endif /* CHAISCRIPT_ENGINE_HPP_ */ - diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index b6cdfe67..4997d667 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_EVAL_HPP_ #define CHAISCRIPT_EVAL_HPP_ @@ -34,31 +33,32 @@ #include "chaiscript_algebraic.hpp" #include "chaiscript_common.hpp" +namespace chaiscript::exception { + class bad_boxed_cast; +} // namespace chaiscript::exception + namespace chaiscript { -namespace exception { -class bad_boxed_cast; -} // namespace exception -} // namespace chaiscript - -namespace chaiscript -{ /// \brief Classes and functions that are part of the runtime eval system - namespace eval - { - template struct AST_Node_Impl; + namespace eval { + template + struct AST_Node_Impl; - template using AST_Node_Impl_Ptr = typename std::unique_ptr>; + template + using AST_Node_Impl_Ptr = typename std::unique_ptr>; - namespace detail - { + namespace detail { /// Helper function that will set up the scope around a function call, including handling the named function parameters template - static Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, const AST_Node_Impl &t_node, const std::vector &t_param_names, const std::vector &t_vals, const std::map *t_locals=nullptr, bool has_this_capture = false) { + Boxed_Value eval_function(chaiscript::detail::Dispatch_Engine &t_ss, + const AST_Node_Impl &t_node, + const std::vector &t_param_names, + const Function_Params &t_vals, + const std::map *t_locals = nullptr, + bool has_this_capture = false) { chaiscript::detail::Dispatch_State state(t_ss); - const Boxed_Value *thisobj = [&]() -> const Boxed_Value *{ - auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); - if (!stack.empty() && stack.back().first == "__this") { + const Boxed_Value *thisobj = [&]() -> const Boxed_Value * { + if (auto &stack = t_ss.get_stack_data(state.stack_holder()).back(); !stack.empty() && stack.back().first == "__this") { return &stack.back().second; } else if (!t_vals.empty()) { return &t_vals[0]; @@ -68,11 +68,13 @@ namespace chaiscript }(); chaiscript::eval::detail::Stack_Push_Pop tpp(state); - if (thisobj && !has_this_capture) { state.add_object("this", *thisobj); } + if (thisobj && !has_this_capture) { + state.add_object("this", *thisobj); + } if (t_locals) { - for (const auto &local : *t_locals) { - state.add_object(local.first, local.second); + for (const auto &[name, value] : *t_locals) { + state.add_object(name, value); } } @@ -86,35 +88,36 @@ namespace chaiscript return t_node.eval(state); } catch (detail::Return_Value &rv) { return std::move(rv.retval); - } + } } - inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) - { - if (!incoming.is_return_value()) - { + inline Boxed_Value clone_if_necessary(Boxed_Value incoming, std::atomic_uint_fast32_t &t_loc, const chaiscript::detail::Dispatch_State &t_ss) { + if (!incoming.is_return_value()) { if (incoming.get_type_info().is_arithmetic()) { return Boxed_Number::clone(incoming); } else if (incoming.get_type_info().bare_equal_type_info(typeid(bool))) { - return Boxed_Value(*static_cast(incoming.get_const_ptr())); + return Boxed_Value(*static_cast(incoming.get_const_ptr())); + } else if (incoming.get_type_info().bare_equal_type_info(typeid(std::string))) { + return Boxed_Value(*static_cast(incoming.get_const_ptr())); } else { - return t_ss->call_function("clone", t_loc, {incoming}, t_ss.conversions()); + std::array params{std::move(incoming)}; + return t_ss->call_function("clone", t_loc, Function_Params{params}, t_ss.conversions()); } } else { incoming.reset_return_value(); return incoming; } } - } + } // namespace detail template - struct AST_Node_Impl : AST_Node - { - AST_Node_Impl(std::string t_ast_node_text, AST_Node_Type t_id, Parse_Location t_loc, - std::vector> t_children = std::vector>()) - : AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc)), - children(std::move(t_children)) - { + struct AST_Node_Impl : AST_Node { + AST_Node_Impl(std::string t_ast_node_text, + AST_Node_Type t_id, + Parse_Location t_loc, + std::vector> t_children = std::vector>()) + : AST_Node(std::move(t_ast_node_text), t_id, std::move(t_loc)) + , children(std::move(t_children)) { } static bool get_scoped_bool_condition(const AST_Node_Impl &node, const chaiscript::detail::Dispatch_State &t_ss) { @@ -122,19 +125,17 @@ namespace chaiscript return get_bool_condition(node.eval(t_ss), t_ss); } - std::vector> get_children() const final { std::vector> retval; retval.reserve(children.size()); - for (auto &&child : children) { + for (auto &child : children) { retval.emplace_back(*child); } return retval; } - Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final - { + Boxed_Value eval(const chaiscript::detail::Dispatch_State &t_e) const final { try { T::trace(t_e, this); return eval_internal(t_e); @@ -146,1417 +147,1348 @@ namespace chaiscript std::vector> children; - protected: - virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const - { - throw std::runtime_error("Undispatched ast_node (internal error)"); - } + protected: + virtual Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const { + throw std::runtime_error("Undispatched ast_node (internal error)"); + } }; - template struct Compiled_AST_Node : AST_Node_Impl { - Compiled_AST_Node(AST_Node_Impl_Ptr t_original_node, std::vector> t_children, - std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func) : - AST_Node_Impl(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children)), - m_func(std::move(t_func)), - m_original_node(std::move(t_original_node)) - { } + Compiled_AST_Node(AST_Node_Impl_Ptr t_original_node, + std::vector> t_children, + std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> t_func) + : AST_Node_Impl(t_original_node->text, AST_Node_Type::Compiled, t_original_node->location, std::move(t_children)) + , m_func(std::move(t_func)) + , m_original_node(std::move(t_original_node)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - return m_func(this->children, t_ss); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return m_func(this->children, t_ss); } - std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func; - AST_Node_Impl_Ptr m_original_node; + std::function> &, const chaiscript::detail::Dispatch_State &t_ss)> m_func; + AST_Node_Impl_Ptr m_original_node; }; - template struct Fold_Right_Binary_Operator_AST_Node : AST_Node_Impl { - Fold_Right_Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children, Boxed_Value t_rhs) : - AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(t_oper)), - m_rhs(std::move(t_rhs)) - { } + Fold_Right_Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children, Boxed_Value t_rhs) + : AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)) + , m_oper(Operators::to_operator(t_oper)) + , m_rhs(std::move(t_rhs)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - return do_oper(t_ss, this->text, this->children[0]->eval(t_ss)); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return do_oper(t_ss, this->text, this->children[0]->eval(t_ss)); + } - protected: - Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, - const std::string &t_oper_string, const Boxed_Value &t_lhs) const - { - try { - if (t_lhs.get_type_info().is_arithmetic()) - { - // If it's an arithmetic operation we want to short circuit dispatch - try{ - return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs); - } catch (const chaiscript::exception::arithmetic_error &) { - throw; - } catch (...) { - throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); - } - } else { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({t_lhs, m_rhs}); - return t_ss->call_function(t_oper_string, m_loc, {t_lhs, m_rhs}, t_ss.conversions()); + protected: + Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, const std::string &t_oper_string, const Boxed_Value &t_lhs) const { + try { + if (t_lhs.get_type_info().is_arithmetic()) { + // If it's an arithmetic operation we want to short circuit dispatch + try { + return Boxed_Number::do_oper(m_oper, t_lhs, m_rhs); + } catch (const chaiscript::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); } + } else { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + std::array params{t_lhs, m_rhs}; + fpp.save_params(Function_Params{params}); + return t_ss->call_function(t_oper_string, m_loc, Function_Params{params}, t_ss.conversions()); } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); - } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); } + } - private: - Operators::Opers m_oper; - Boxed_Value m_rhs; - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + Operators::Opers m_oper; + Boxed_Value m_rhs; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - template struct Binary_Operator_AST_Node : AST_Node_Impl { - Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(t_oper)) - { } + Binary_Operator_AST_Node(const std::string &t_oper, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(t_oper, AST_Node_Type::Binary, std::move(t_loc), std::move(t_children)) + , m_oper(Operators::to_operator(t_oper)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - auto lhs = this->children[0]->eval(t_ss); - auto rhs = this->children[1]->eval(t_ss); - return do_oper(t_ss, m_oper, this->text, lhs, rhs); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + auto lhs = this->children[0]->eval(t_ss); + auto rhs = this->children[1]->eval(t_ss); + return do_oper(t_ss, m_oper, this->text, lhs, rhs); + } - protected: - Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, - Operators::Opers t_oper, const std::string &t_oper_string, const Boxed_Value &t_lhs, const Boxed_Value &t_rhs) const - { - try { - if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) - { - // If it's an arithmetic operation we want to short circuit dispatch - try{ - return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs); - } catch (const chaiscript::exception::arithmetic_error &) { - throw; - } catch (...) { - throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); - } - } else { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({t_lhs, t_rhs}); - return t_ss->call_function(t_oper_string, m_loc, {t_lhs, t_rhs}, t_ss.conversions()); + protected: + Boxed_Value do_oper(const chaiscript::detail::Dispatch_State &t_ss, + Operators::Opers t_oper, + const std::string &t_oper_string, + const Boxed_Value &t_lhs, + const Boxed_Value &t_rhs) const { + try { + if (t_oper != Operators::Opers::invalid && t_lhs.get_type_info().is_arithmetic() && t_rhs.get_type_info().is_arithmetic()) { + // If it's an arithmetic operation we want to short circuit dispatch + try { + return Boxed_Number::do_oper(t_oper, t_lhs, t_rhs); + } catch (const chaiscript::exception::arithmetic_error &) { + throw; + } catch (...) { + throw exception::eval_error("Error with numeric operator calling: " + t_oper_string); } + } else { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + std::array params{t_lhs, t_rhs}; + fpp.save_params(Function_Params(params)); + return t_ss->call_function(t_oper_string, m_loc, Function_Params(params), t_ss.conversions()); } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); - } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate '" + t_oper_string + "' operator.", e.parameters, e.functions, false, *t_ss); } + } - private: - Operators::Opers m_oper; - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; - template struct Constant_AST_Node final : AST_Node_Impl { Constant_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, Boxed_Value t_value) - : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc)), - m_value(std::move(t_value)) - { + : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Constant, std::move(t_loc)) + , m_value(std::move(t_value)) { } explicit Constant_AST_Node(Boxed_Value t_value) - : AST_Node_Impl("", AST_Node_Type::Constant, Parse_Location()), - m_value(std::move(t_value)) - { + : AST_Node_Impl("", AST_Node_Type::Constant, Parse_Location()) + , m_value(std::move(t_value)) { } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { - return m_value; - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { return m_value; } Boxed_Value m_value; }; template struct Id_AST_Node final : AST_Node_Impl { - Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) : - AST_Node_Impl(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)) - { } + Id_AST_Node(const std::string &t_ast_node_text, Parse_Location t_loc) + : AST_Node_Impl(t_ast_node_text, AST_Node_Type::Id, std::move(t_loc)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - try { - return t_ss.get_object(this->text, m_loc); - } - catch (std::exception &) { - throw exception::eval_error("Can not find object: " + this->text); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + try { + return t_ss.get_object(this->text, m_loc); + } catch (std::exception &) { + throw exception::eval_error("Can not find object: " + this->text); } + } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Fun_Call_AST_Node : AST_Node_Impl { - Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { - assert(!this->children.empty()); - } + Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Fun_Call, std::move(t_loc), std::move(t_children)) { + assert(!this->children.empty()); + } - template - Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const - { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + template + Boxed_Value do_eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - std::vector params; + std::vector params; - params.reserve(this->children[1]->children.size()); - for (const auto &child : this->children[1]->children) { - params.push_back(child->eval(t_ss)); - } + params.reserve(this->children[1]->children.size()); + for (const auto &child : this->children[1]->children) { + params.push_back(child->eval(t_ss)); + } - if (Save_Params) { - fpp.save_params(params); - } + if (Save_Params) { + fpp.save_params(Function_Params{params}); + } - Boxed_Value fn(this->children[0]->eval(t_ss)); + Boxed_Value fn(this->children[0]->eval(t_ss)); - using ConstFunctionTypePtr = const dispatch::Proxy_Function_Base *; + try { + return (*t_ss->boxed_cast(fn))(Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", + e.parameters, + e.functions, + false, + *t_ss); + } catch (const exception::bad_boxed_cast &) { try { - return (*t_ss->boxed_cast(fn))(params, t_ss.conversions()); - } - catch(const exception::dispatch_error &e){ - throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'", e.parameters, e.functions, false, *t_ss); - } - catch(const exception::bad_boxed_cast &){ - try { - using ConstFunctionTypeRef = const Const_Proxy_Function &; - Const_Proxy_Function f = t_ss->boxed_cast(fn); - // handle the case where there is only 1 function to try to call and dispatch fails on it - throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, {f}, false, *t_ss); - } catch (const exception::bad_boxed_cast &) { - throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); - } - } - catch(const exception::arity_error &e){ - throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); - } - catch(const exception::guard_error &e){ - throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); - } - catch(detail::Return_Value &rv) { - return rv.retval; + using ConstFunctionTypeRef = const Const_Proxy_Function &; + Const_Proxy_Function f = t_ss->boxed_cast(fn); + // handle the case where there is only 1 function to try to call and dispatch fails on it + throw exception::eval_error("Error calling function '" + this->children[0]->text + "'", params, make_vector(f), false, *t_ss); + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("'" + this->children[0]->pretty_print() + "' does not evaluate to a function."); } + } catch (const exception::arity_error &e) { + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } catch (const exception::guard_error &e) { + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } catch (detail::Return_Value &rv) { + return rv.retval; } + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - return do_eval_internal(t_ss); - } - + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { return do_eval_internal(t_ss); } }; - template struct Unused_Return_Fun_Call_AST_Node final : Fun_Call_AST_Node { - Unused_Return_Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - Fun_Call_AST_Node(std::move(t_ast_node_text), std::move(t_loc), std::move(t_children)) { } + Unused_Return_Fun_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : Fun_Call_AST_Node(std::move(t_ast_node_text), std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - return this->template do_eval_internal(t_ss); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return this->template do_eval_internal(t_ss); + } }; - - - - template struct Arg_AST_Node final : AST_Node_Impl { - Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } - + Arg_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { + } }; template struct Arg_List_AST_Node final : AST_Node_Impl { - Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { } + Arg_List_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Arg_List, std::move(t_loc), std::move(t_children)) { + } + static std::string get_arg_name(const AST_Node_Impl &t_node) { + if (t_node.children.empty()) { + return t_node.text; + } else if (t_node.children.size() == 1) { + return t_node.children[0]->text; + } else { + return t_node.children[1]->text; + } + } - static std::string get_arg_name(const AST_Node_Impl &t_node) { - if (t_node.children.empty()) - { - return t_node.text; - } else if (t_node.children.size() == 1) { - return t_node.children[0]->text; - } else { - return t_node.children[1]->text; - } + static std::vector get_arg_names(const AST_Node_Impl &t_node) { + std::vector retval; + + for (const auto &node : t_node.children) { + retval.push_back(get_arg_name(*node)); } - static std::vector get_arg_names(const AST_Node_Impl &t_node) { - std::vector retval; + return retval; + } - for (const auto &node : t_node.children) - { - retval.push_back(get_arg_name(*node)); - } + static std::pair get_arg_type(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + if (t_node.children.size() < 2) { + return {}; + } else { + return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)}; + } + } - return retval; + static dispatch::Param_Types get_arg_types(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { + std::vector> retval; + + for (const auto &child : t_node.children) { + retval.push_back(get_arg_type(*child, t_ss)); } - static std::pair get_arg_type(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) - { - if (t_node.children.size() < 2) - { - return {}; - } else { - return {t_node.children[0]->text, t_ss->get_type(t_node.children[0]->text, false)}; - } - } - - static dispatch::Param_Types get_arg_types(const AST_Node_Impl &t_node, const chaiscript::detail::Dispatch_State &t_ss) { - std::vector> retval; - - for (const auto &child : t_node.children) - { - retval.push_back(get_arg_type(*child, t_ss)); - } - - return dispatch::Param_Types(std::move(retval)); - } + return dispatch::Param_Types(std::move(retval)); + } }; template struct Equation_AST_Node final : AST_Node_Impl { - Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(this->text)) - { assert(this->children.size() == 2); } + Equation_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Equation, std::move(t_loc), std::move(t_children)) + , m_oper(Operators::to_operator(this->text)) { + assert(this->children.size() == 2); + } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - Boxed_Value rhs = this->children[1]->eval(t_ss); - Boxed_Value lhs = this->children[0]->eval(t_ss); + auto params = [&]() { + // The RHS *must* be evaluated before the LHS + // consider `var range = range(x)` + // if we declare the variable in scope first, then the name lookup fails + // for the RHS + auto rhs = this->children[1]->eval(t_ss); + auto lhs = this->children[0]->eval(t_ss); + std::array p{std::move(lhs), std::move(rhs)}; + return p; + }(); - if (lhs.is_return_value()) { - throw exception::eval_error("Error, cannot assign to temporary value."); - } else if (lhs.is_const()) { - throw exception::eval_error("Error, cannot assign to constant value."); - } - - - if (m_oper != Operators::Opers::invalid && lhs.get_type_info().is_arithmetic() && - rhs.get_type_info().is_arithmetic()) - { - try { - return Boxed_Number::do_oper(m_oper, lhs, rhs); - } catch (const std::exception &) { - throw exception::eval_error("Error with unsupported arithmetic assignment operation."); - } - } else if (m_oper == Operators::Opers::assign) { - try { - - if (lhs.is_undef()) { - if (!this->children.empty() - && ((this->children[0]->identifier == AST_Node_Type::Reference) - || (!this->children[0]->children.empty() - && this->children[0]->children[0]->identifier == AST_Node_Type::Reference) - ) - ) - - { - /// \todo This does not handle the case of an unassigned reference variable - /// being assigned outside of its declaration - lhs.assign(rhs); - lhs.reset_return_value(); - return rhs; - } else { - rhs = detail::clone_if_necessary(std::move(rhs), m_clone_loc, t_ss); - } - } - - try { - return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); - } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); - } - } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Missing clone or copy constructor for right hand side of equation", e.parameters, e.functions, false, *t_ss); - } - } - else if (this->text == ":=") { - if (lhs.is_undef() || Boxed_Value::type_match(lhs, rhs)) { - lhs.assign(rhs); - lhs.reset_return_value(); - } else { - throw exception::eval_error("Mismatched types in equation"); - } - } - else { - try { - return t_ss->call_function(this->text, m_loc, {std::move(lhs), rhs}, t_ss.conversions()); - } catch(const exception::dispatch_error &e){ - throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); - } - } - - return rhs; + if (params[0].is_return_value()) { + throw exception::eval_error("Error, cannot assign to temporary value."); + } else if (params[0].is_const()) { + throw exception::eval_error("Error, cannot assign to constant value."); } - private: - Operators::Opers m_oper; - mutable std::atomic_uint_fast32_t m_loc = {0}; - mutable std::atomic_uint_fast32_t m_clone_loc = {0}; + if (m_oper != Operators::Opers::invalid && params[0].get_type_info().is_arithmetic() && params[1].get_type_info().is_arithmetic()) { + try { + return Boxed_Number::do_oper(m_oper, params[0], params[1]); + } catch (const std::exception &) { + throw exception::eval_error("Error with unsupported arithmetic assignment operation."); + } + } else if (m_oper == Operators::Opers::assign) { + try { + if (params[0].is_undef()) { + if (!this->children.empty() + && ((this->children[0]->identifier == AST_Node_Type::Reference) + || (!this->children[0]->children.empty() && this->children[0]->children[0]->identifier == AST_Node_Type::Reference))) + + { + /// \todo This does not handle the case of an unassigned reference variable + /// being assigned outside of its declaration + params[0].assign(params[1]); + params[0].reset_return_value(); + return params[1]; + } else { + params[1] = detail::clone_if_necessary(std::move(params[1]), m_clone_loc, t_ss); + } + } + + try { + return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Missing clone or copy constructor for right hand side of equation", + e.parameters, + e.functions, + false, + *t_ss); + } + } else if (this->text == ":=") { + if (params[0].is_undef() || Boxed_Value::type_match(params[0], params[1])) { + params[0].assign(params[1]); + params[0].reset_return_value(); + } else { + throw exception::eval_error("Mismatched types in equation"); + } + } else { + try { + return t_ss->call_function(this->text, m_loc, Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Unable to find appropriate'" + this->text + "' operator.", e.parameters, e.functions, false, *t_ss); + } + } + + return params[1]; + } + + private: + Operators::Opers m_oper; + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_clone_loc = {0}; }; template struct Global_Decl_AST_Node final : AST_Node_Impl { - Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { } + Global_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Global_Decl, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const std::string &idname = - [&]()->const std::string & { - if (this->children[0]->identifier == AST_Node_Type::Reference) { - return this->children[0]->children[0]->text; - } else { - return this->children[0]->text; - } - }(); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const std::string &idname = [&]() -> const std::string & { + if (this->children[0]->identifier == AST_Node_Type::Reference) { + return this->children[0]->children[0]->text; + } else { + return this->children[0]->text; + } + }(); - return t_ss->add_global_no_throw(Boxed_Value(), idname); - - } + return t_ss->add_global_no_throw(Boxed_Value(), idname); + } }; - template struct Var_Decl_AST_Node final : AST_Node_Impl { - Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { } + Var_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Var_Decl, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const std::string &idname = this->children[0]->text; + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; - try { - Boxed_Value bv; - t_ss.add_object(idname, bv); - return bv; - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Variable redefined '" + e.name() + "'"); - } + try { + Boxed_Value bv; + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); } + } }; template struct Assign_Decl_AST_Node final : AST_Node_Impl { - Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) { } + Assign_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Assign_Decl, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const std::string &idname = this->children[0]->text; + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const std::string &idname = this->children[0]->text; - try { - Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss)); - bv.reset_return_value(); - t_ss.add_object(idname, bv); - return bv; - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Variable redefined '" + e.name() + "'"); - } + try { + Boxed_Value bv(detail::clone_if_necessary(this->children[1]->eval(t_ss), m_loc, t_ss)); + bv.reset_return_value(); + t_ss.add_object(idname, bv); + return bv; + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Variable redefined '" + e.name() + "'"); } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; - }; + } + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; + }; template struct Array_Call_AST_Node final : AST_Node_Impl { - Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { } + Array_Call_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Array_Call, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - const std::vector params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)}; + std::array params{this->children[0]->eval(t_ss), this->children[1]->eval(t_ss)}; - try { - fpp.save_params(params); - return t_ss->call_function("[]", m_loc, params, t_ss.conversions()); - } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss ); - } + try { + fpp.save_params(Function_Params{params}); + return t_ss->call_function("[]", m_loc, Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, false, *t_ss); } + } - - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Dot_Access_AST_Node final : AST_Node_Impl { - Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)), - m_fun_name( - ((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call))? - this->children[1]->children[0]->text:this->children[1]->text) { } + Dot_Access_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Dot_Access, std::move(t_loc), std::move(t_children)) + , m_fun_name(((this->children[1]->identifier == AST_Node_Type::Fun_Call) || (this->children[1]->identifier == AST_Node_Type::Array_Call)) + ? this->children[1]->children[0]->text + : this->children[1]->text) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); + Boxed_Value retval = this->children[0]->eval(t_ss); + auto params = make_vector(retval); - Boxed_Value retval = this->children[0]->eval(t_ss); - std::vector params{retval}; - - bool has_function_params = false; - if (this->children[1]->children.size() > 1) { - has_function_params = true; - for (const auto &child : this->children[1]->children[1]->children) { - params.push_back(child->eval(t_ss)); - } + bool has_function_params = false; + if (this->children[1]->children.size() > 1) { + has_function_params = true; + for (const auto &child : this->children[1]->children[1]->children) { + params.push_back(child->eval(t_ss)); } - - fpp.save_params(params); - - try { - retval = t_ss->call_member(m_fun_name, m_loc, std::move(params), has_function_params, t_ss.conversions()); - } - catch(const exception::dispatch_error &e){ - if (e.functions.empty()) - { - throw exception::eval_error("'" + m_fun_name + "' is not a function."); - } else { - throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss); - } - } - catch(detail::Return_Value &rv) { - retval = std::move(rv.retval); - } - - if (this->children[1]->identifier == AST_Node_Type::Array_Call) { - try { - retval = t_ss->call_function("[]", m_array_loc, {retval, this->children[1]->children[1]->eval(t_ss)}, t_ss.conversions()); - } - catch(const exception::dispatch_error &e){ - throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); - } - } - - return retval; } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; - mutable std::atomic_uint_fast32_t m_array_loc = {0}; - const std::string m_fun_name; - }; + fpp.save_params(Function_Params{params}); + try { + retval = t_ss->call_member(m_fun_name, m_loc, Function_Params{params}, has_function_params, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + if (e.functions.empty()) { + throw exception::eval_error("'" + m_fun_name + "' is not a function."); + } else { + throw exception::eval_error(std::string(e.what()) + " for function '" + m_fun_name + "'", e.parameters, e.functions, true, *t_ss); + } + } catch (detail::Return_Value &rv) { + retval = std::move(rv.retval); + } + + if (this->children[1]->identifier == AST_Node_Type::Array_Call) { + try { + std::array p{retval, this->children[1]->children[1]->eval(t_ss)}; + retval = t_ss->call_function("[]", m_array_loc, Function_Params{p}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate array lookup operator '[]'.", e.parameters, e.functions, true, *t_ss); + } + } + + return retval; + } + + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_array_loc = {0}; + const std::string m_fun_name; + }; template struct Lambda_AST_Node final : AST_Node_Impl { - Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(t_ast_node_text, - AST_Node_Type::Lambda, - std::move(t_loc), - std::vector>(std::make_move_iterator(t_children.begin()), - std::make_move_iterator(std::prev(t_children.end()))) - ), - m_param_names(Arg_List_AST_Node::get_arg_names(*this->children[1])), - m_this_capture(has_this_capture(this->children[0]->children)), - m_lambda_node(std::move(t_children.back())) - { } + Lambda_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(t_ast_node_text, + AST_Node_Type::Lambda, + std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator(std::prev(t_children.end())))) + , m_param_names(Arg_List_AST_Node::get_arg_names(*this->children[1])) + , m_this_capture(has_this_capture(this->children[0]->children)) + , m_lambda_node(std::move(t_children.back())) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const auto captures = [&]() -> std::map { + std::map named_captures; + for (const auto &capture : this->children[0]->children) { + named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss))); + } + return named_captures; + }(); - const auto captures = [&]()->std::map{ - std::map named_captures; - for (const auto &capture : this->children[0]->children) { - named_captures.insert(std::make_pair(capture->children[0]->text, capture->children[0]->eval(t_ss))); - } - return named_captures; - }(); + const auto numparams = this->children[1]->children.size(); + const auto param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); - const auto numparams = this->children[1]->children.size(); - const auto param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); + std::reference_wrapper engine(*t_ss); - std::reference_wrapper engine(*t_ss); + return Boxed_Value(dispatch::make_dynamic_proxy_function( + [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, this_capture = this->m_this_capture]( + const Function_Params &t_params) { + return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture); + }, + static_cast(numparams), + m_lambda_node, + param_types)); + } - return Boxed_Value( - dispatch::make_dynamic_proxy_function( - [engine, lambda_node = this->m_lambda_node, param_names = this->m_param_names, captures, - this_capture = this->m_this_capture] (const std::vector &t_params) - { - return detail::eval_function(engine, *lambda_node, param_names, t_params, &captures, this_capture); - }, - static_cast(numparams), m_lambda_node, param_types - ) - ); - } + static bool has_this_capture(const std::vector> &t_children) noexcept { + return std::any_of(std::begin(t_children), std::end(t_children), [](const auto &child) { return child->children[0]->text == "this"; }); + } - static bool has_this_capture(const std::vector> &children) { - return std::any_of(std::begin(children), std::end(children), - [](const auto &child){ - return child->children[0]->text == "this"; - } - ); - } - - private: - const std::vector m_param_names; - const bool m_this_capture = false; - const std::shared_ptr> m_lambda_node; + private: + const std::vector m_param_names; + const bool m_this_capture = false; + const std::shared_ptr> m_lambda_node; }; template struct Scopeless_Block_AST_Node final : AST_Node_Impl { - Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) { } + Scopeless_Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Scopeless_Block, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - const auto num_children = this->children.size(); - for (size_t i = 0; i < num_children-1; ++i) { - this->children[i]->eval(t_ss); - } - return this->children.back()->eval(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const auto num_children = this->children.size(); + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); } + return this->children.back()->eval(t_ss); + } }; template struct Block_AST_Node final : AST_Node_Impl { - Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { } + Block_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Block, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - const auto num_children = this->children.size(); - for (size_t i = 0; i < num_children-1; ++i) { - this->children[i]->eval(t_ss); - } - return this->children.back()->eval(t_ss); + const auto num_children = this->children.size(); + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); } + return this->children.back()->eval(t_ss); + } }; template struct Def_AST_Node final : AST_Node_Impl { + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; - std::shared_ptr> m_body_node; - std::shared_ptr> m_guard_node; + Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Def, + std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator( + std::prev(t_children.end(), has_guard(t_children, 1) ? 2 : 1)))) + , + // This apparent use after move is safe because we are only moving out the specific elements we need + // on each operation. + m_body_node(get_body_node(std::move(t_children))) + , m_guard_node(get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2)) - Def_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Def, std::move(t_loc), - std::vector>(std::make_move_iterator(t_children.begin()), - std::make_move_iterator(std::prev(t_children.end(), has_guard(t_children, 1)?2:1))) - ), - // This apparent use after move is safe because we are only moving out the specific elements we need - // on each operation. - m_body_node(get_body_node(std::move(t_children))), - m_guard_node(get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) + { + } - { } + static std::shared_ptr> get_guard_node(std::vector> &&vec, bool has_guard) { + if (has_guard) { + return std::move(*std::prev(vec.end(), 2)); + } else { + return {}; + } + } - static std::shared_ptr> get_guard_node(std::vector> &&vec, bool has_guard) - { - if (has_guard) { - return std::move(*std::prev(vec.end(), 2)); - } else { - return {}; + static std::shared_ptr> get_body_node(std::vector> &&vec) { return std::move(vec.back()); } + + static bool has_guard(const std::vector> &t_children, const std::size_t offset) noexcept { + if ((t_children.size() > 2 + offset) && (t_children[1 + offset]->identifier == AST_Node_Type::Arg_List)) { + if (t_children.size() > 3 + offset) { + return true; + } + } else { + if (t_children.size() > 2 + offset) { + return true; } } + return false; + } - static std::shared_ptr> get_body_node(std::vector> &&vec) - { - return std::move(vec.back()); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + std::vector t_param_names; + size_t numparams = 0; + + dispatch::Param_Types param_types; + + if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { + numparams = this->children[1]->children.size(); + t_param_names = Arg_List_AST_Node::get_arg_names(*this->children[1]); + param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); } - static bool has_guard(const std::vector> &t_children, const std::size_t offset) - { - if ((t_children.size() > 2 + offset) && (t_children[1+offset]->identifier == AST_Node_Type::Arg_List)) { - if (t_children.size() > 3 + offset) { - return true; - } - } - else { - if (t_children.size() > 2 + offset) { - return true; - } - } - return false; + std::reference_wrapper engine(*t_ss); + std::shared_ptr guard; + if (m_guard_node) { + guard = dispatch::make_dynamic_proxy_function( + [engine, guardnode = m_guard_node, t_param_names](const Function_Params &t_params) { + return detail::eval_function(engine, *guardnode, t_param_names, t_params); + }, + static_cast(numparams), + m_guard_node); } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - std::vector t_param_names; - size_t numparams = 0; - - dispatch::Param_Types param_types; - - if ((this->children.size() > 1) && (this->children[1]->identifier == AST_Node_Type::Arg_List)) { - numparams = this->children[1]->children.size(); - t_param_names = Arg_List_AST_Node::get_arg_names(*this->children[1]); - param_types = Arg_List_AST_Node::get_arg_types(*this->children[1], t_ss); - } - - std::reference_wrapper engine(*t_ss); - std::shared_ptr guard; - if (m_guard_node) { - guard = dispatch::make_dynamic_proxy_function( - [engine, guardnode = m_guard_node, t_param_names](const std::vector &t_params) - { - return detail::eval_function(engine, *guardnode, t_param_names, t_params); - }, - static_cast(numparams), m_guard_node); - } - - try { - const std::string & l_function_name = this->children[0]->text; - t_ss->add( - dispatch::make_dynamic_proxy_function( - [engine, func_node = m_body_node, t_param_names](const std::vector &t_params) - { - return detail::eval_function(engine, *func_node, t_param_names, t_params); - }, - static_cast(numparams), m_body_node, - param_types, guard), l_function_name); - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Function redefined '" + e.name() + "'"); - } - return void_var(); + try { + const std::string &l_function_name = this->children[0]->text; + t_ss->add(dispatch::make_dynamic_proxy_function( + [engine, func_node = m_body_node, t_param_names](const Function_Params &t_params) { + return detail::eval_function(engine, *func_node, t_param_names, t_params); + }, + static_cast(numparams), + m_body_node, + param_types, + guard), + l_function_name); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Function redefined '" + e.name() + "'"); } - + return void_var(); + } }; template struct While_AST_Node final : AST_Node_Impl { - While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { } + While_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::While, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - try { - while (this->get_scoped_bool_condition(*this->children[0], t_ss)) { - try { - this->children[1]->eval(t_ss); - } catch (detail::Continue_Loop &) { - // we got a continue exception, which means all of the remaining - // loop implementation is skipped and we just need to continue to - // the next condition test - } - } - } catch (detail::Break_Loop &) { - // loop was broken intentionally + try { + while (this->get_scoped_bool_condition(*this->children[0], t_ss)) { + try { + this->children[1]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next condition test + } } - - return void_var(); + } catch (detail::Break_Loop &) { + // loop was broken intentionally } + + return void_var(); + } }; template struct Class_AST_Node final : AST_Node_Impl { - Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { } + Class_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Class, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - /// \todo do this better - // put class name in current scope so it can be looked up by the attrs and methods - t_ss.add_object("_current_class_name", const_var(this->children[0]->text)); + /// \todo do this better + // put class name in current scope so it can be looked up by the attrs and methods + t_ss.add_object("_current_class_name", const_var(this->children[0]->text)); - this->children[1]->eval(t_ss); + this->children[1]->eval(t_ss); - return void_var(); - } + return void_var(); + } }; - template struct If_AST_Node final : AST_Node_Impl { - If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) - { - assert(this->children.size() == 3); - } + If_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::If, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 3); + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { - return this->children[1]->eval(t_ss); - } else { - return this->children[2]->eval(t_ss); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + if (this->get_bool_condition(this->children[0]->eval(t_ss), t_ss)) { + return this->children[1]->eval(t_ss); + } else { + return this->children[2]->eval(t_ss); } + } }; template struct Ranged_For_AST_Node final : AST_Node_Impl { - Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 3); } + Ranged_For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Ranged_For, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 3); + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint){ - uint_fast32_t hint = t_hint; - auto funs = t_ss->get_function(t_name, hint); - if (funs.first != hint) { t_hint = uint_fast32_t(funs.first); } - return std::move(funs.second); - }; - - const auto call_function = [&t_ss](const auto &t_funcs, const Boxed_Value &t_param) { - return dispatch::dispatch(*t_funcs, {t_param}, t_ss.conversions()); - }; - - - const std::string &loop_var_name = this->children[0]->text; - Boxed_Value range_expression_result = this->children[1]->eval(t_ss); - - - const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing){ - try { - for (auto &&loop_var : ranged_thing) { - // This scope push and pop might not be the best thing for perf - // but we know it's 100% correct - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - /// to-do make this if-constexpr with C++17 branch - if (!std::is_same, Boxed_Value>::value) { - t_ss.add_get_object(loop_var_name, Boxed_Value(std::ref(loop_var))); - } else { - t_ss.add_get_object(loop_var_name, Boxed_Value(loop_var)); - } - try { - this->children[2]->eval(t_ss); - } catch (detail::Continue_Loop &) { - } - } - } catch (detail::Break_Loop &) { - // loop broken - } - return void_var(); - }; - - if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector))) { - return do_loop(boxed_cast &>(range_expression_result)); - } else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map))) { - return do_loop(boxed_cast &>(range_expression_result)); - } else { - const auto range_funcs = get_function("range", m_range_loc); - const auto empty_funcs = get_function("empty", m_empty_loc); - const auto front_funcs = get_function("front", m_front_loc); - const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc); - - try { - const auto range_obj = call_function(range_funcs, range_expression_result); - while (!boxed_cast(call_function(empty_funcs, range_obj))) { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - t_ss.add_get_object(loop_var_name, call_function(front_funcs, range_obj)); - try { - this->children[2]->eval(t_ss); - } catch (detail::Continue_Loop &) { - } - call_function(pop_front_funcs, range_obj); - } - } catch (detail::Break_Loop &) { - // loop broken - } - return void_var(); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + const auto get_function = [&t_ss](const std::string &t_name, auto &t_hint) { + uint_fast32_t hint = t_hint; + auto [funs_loc, funs] = t_ss->get_function(t_name, hint); + if (funs_loc != hint) { + t_hint = uint_fast32_t(funs_loc); } + return std::move(funs); + }; - } + 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()); + }; - private: - mutable std::atomic_uint_fast32_t m_range_loc = {0}; - mutable std::atomic_uint_fast32_t m_empty_loc = {0}; - mutable std::atomic_uint_fast32_t m_front_loc = {0}; - mutable std::atomic_uint_fast32_t m_pop_front_loc = {0}; - }; - - - template - struct For_AST_Node final : AST_Node_Impl { - For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 4); } - - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + const std::string &loop_var_name = this->children[0]->text; + Boxed_Value range_expression_result = this->children[1]->eval(t_ss); + const auto do_loop = [&loop_var_name, &t_ss, this](const auto &ranged_thing) { try { - for ( - this->children[0]->eval(t_ss); - this->get_scoped_bool_condition(*this->children[1], t_ss); - this->children[2]->eval(t_ss) - ) { + for (auto &&loop_var : ranged_thing) { + // This scope push and pop might not be the best thing for perf + // but we know it's 100% correct + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + /// to-do make this if-constexpr with C++17 branch + if (!std::is_same, Boxed_Value>::value) { + t_ss.add_get_object(loop_var_name, Boxed_Value(std::ref(loop_var))); + } else { + t_ss.add_get_object(loop_var_name, Boxed_Value(loop_var)); + } try { - // Body of Loop - this->children[3]->eval(t_ss); + this->children[2]->eval(t_ss); } catch (detail::Continue_Loop &) { - // we got a continue exception, which means all of the remaining - // loop implementation is skipped and we just need to continue to - // the next iteration step } } } catch (detail::Break_Loop &) { // loop broken } + return void_var(); + }; + if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::vector))) { + return do_loop(boxed_cast &>(range_expression_result)); + } else if (range_expression_result.get_type_info().bare_equal_type_info(typeid(std::map))) { + return do_loop(boxed_cast &>(range_expression_result)); + } else { + const auto range_funcs = get_function("range", m_range_loc); + const auto empty_funcs = get_function("empty", m_empty_loc); + const auto front_funcs = get_function("front", m_front_loc); + const auto pop_front_funcs = get_function("pop_front", m_pop_front_loc); + + try { + const auto range_obj = call_function(range_funcs, range_expression_result); + while (!boxed_cast(call_function(empty_funcs, range_obj))) { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + t_ss.add_get_object(loop_var_name, call_function(front_funcs, range_obj)); + try { + this->children[2]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // continue statement hit + } + call_function(pop_front_funcs, range_obj); + } + } catch (detail::Break_Loop &) { + // loop broken + } return void_var(); } + } + private: + mutable std::atomic_uint_fast32_t m_range_loc = {0}; + mutable std::atomic_uint_fast32_t m_empty_loc = {0}; + mutable std::atomic_uint_fast32_t m_front_loc = {0}; + mutable std::atomic_uint_fast32_t m_pop_front_loc = {0}; + }; + + template + struct For_AST_Node final : AST_Node_Impl { + For_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::For, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 4); + } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + for (this->children[0]->eval(t_ss); this->get_scoped_bool_condition(*this->children[1], t_ss); this->children[2]->eval(t_ss)) { + try { + // Body of Loop + this->children[3]->eval(t_ss); + } catch (detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next iteration step + } + } + } catch (detail::Break_Loop &) { + // loop broken + } + + return void_var(); + } }; template struct Switch_AST_Node final : AST_Node_Impl { - Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { } + Switch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Switch, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - bool breaking = false; - size_t currentCase = 1; - bool hasMatched = false; + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + bool breaking = false; + size_t currentCase = 1; + bool hasMatched = false; - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - Boxed_Value match_value(this->children[0]->eval(t_ss)); + Boxed_Value match_value(this->children[0]->eval(t_ss)); - while (!breaking && (currentCase < this->children.size())) { - try { - if (this->children[currentCase]->identifier == AST_Node_Type::Case) { - //This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. - try { - if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, {match_value, this->children[currentCase]->children[0]->eval(t_ss)}, t_ss.conversions()))) { - this->children[currentCase]->eval(t_ss); - hasMatched = true; - } - } - catch (const exception::bad_boxed_cast &) { - throw exception::eval_error("Internal error: case guard evaluation not boolean"); + while (!breaking && (currentCase < this->children.size())) { + try { + if (this->children[currentCase]->identifier == AST_Node_Type::Case) { + // This is a little odd, but because want to see both the switch and the case simultaneously, I do a downcast here. + try { + std::array p{match_value, this->children[currentCase]->children[0]->eval(t_ss)}; + if (hasMatched || boxed_cast(t_ss->call_function("==", m_loc, Function_Params{p}, t_ss.conversions()))) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; } + } catch (const exception::bad_boxed_cast &) { + throw exception::eval_error("Internal error: case guard evaluation not boolean"); } - else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { - this->children[currentCase]->eval(t_ss); - hasMatched = true; - } + } else if (this->children[currentCase]->identifier == AST_Node_Type::Default) { + this->children[currentCase]->eval(t_ss); + hasMatched = true; } - catch (detail::Break_Loop &) { - breaking = true; - } - ++currentCase; + } catch (detail::Break_Loop &) { + breaking = true; } - return void_var(); + ++currentCase; } + return void_var(); + } - mutable std::atomic_uint_fast32_t m_loc = {0}; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Case_AST_Node final : AST_Node_Impl { - Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 2); /* how many children does it have? */ } + Case_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Case, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 2); /* how many children does it have? */ + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - this->children[1]->eval(t_ss); + this->children[1]->eval(t_ss); - return void_var(); - } + return void_var(); + } }; - + template struct Default_AST_Node final : AST_Node_Impl { - Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 1); } + Default_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Default, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 1); + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - this->children[0]->eval(t_ss); + this->children[0]->eval(t_ss); - return void_var(); - } + return void_var(); + } }; - template struct Inline_Array_AST_Node final : AST_Node_Impl { - Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { } + Inline_Array_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Array, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - try { - std::vector vec; - if (!this->children.empty()) { - vec.reserve(this->children[0]->children.size()); - for (const auto &child : this->children[0]->children) { - vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss)); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + try { + std::vector vec; + if (!this->children.empty()) { + vec.reserve(this->children[0]->children.size()); + for (const auto &child : this->children[0]->children) { + vec.push_back(detail::clone_if_necessary(child->eval(t_ss), m_loc, t_ss)); } - return const_var(std::move(vec)); - } - catch (const exception::dispatch_error &) { - throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements"); } + return const_var(std::move(vec)); + } catch (const exception::dispatch_error &) { + throw exception::eval_error("Can not find appropriate 'clone' or copy constructor for vector elements"); } + } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Inline_Map_AST_Node final : AST_Node_Impl { - Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { } + Inline_Map_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Map, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - try { - std::map retval; + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + try { + std::map retval; - for (const auto &child : this->children[0]->children) { - retval.insert(std::make_pair(t_ss->boxed_cast(child->children[0]->eval(t_ss)), - detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss))); - } - - return const_var(std::move(retval)); - } - catch (const exception::dispatch_error &e) { - throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.", e.parameters, e.functions, false, *t_ss); + for (const auto &child : this->children[0]->children) { + retval.insert(std::make_pair(t_ss->boxed_cast(child->children[0]->eval(t_ss)), + detail::clone_if_necessary(child->children[1]->eval(t_ss), m_loc, t_ss))); } + + return const_var(std::move(retval)); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Can not find appropriate copy constructor or 'clone' while inserting into Map.", + e.parameters, + e.functions, + false, + *t_ss); } + } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Return_AST_Node final : AST_Node_Impl { - Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { } + Return_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Return, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - if (!this->children.empty()) { - throw detail::Return_Value{this->children[0]->eval(t_ss)}; - } - else { - throw detail::Return_Value{void_var()}; - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + if (!this->children.empty()) { + throw detail::Return_Value{this->children[0]->eval(t_ss)}; + } else { + throw detail::Return_Value{void_var()}; } + } }; template struct File_AST_Node final : AST_Node_Impl { - File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { } + File_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - try { - const auto num_children = this->children.size(); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + try { + const auto num_children = this->children.size(); - if (num_children > 0) { - for (size_t i = 0; i < num_children-1; ++i) { - this->children[i]->eval(t_ss); - } - return this->children.back()->eval(t_ss); - } else { - return void_var(); + if (num_children > 0) { + for (size_t i = 0; i < num_children - 1; ++i) { + this->children[i]->eval(t_ss); } - } catch (const detail::Continue_Loop &) { - throw exception::eval_error("Unexpected `continue` statement outside of a loop"); - } catch (const detail::Break_Loop &) { - throw exception::eval_error("Unexpected `break` statement outside of a loop"); + return this->children.back()->eval(t_ss); + } else { + return void_var(); } + } catch (const detail::Continue_Loop &) { + throw exception::eval_error("Unexpected `continue` statement outside of a loop"); + } catch (const detail::Break_Loop &) { + throw exception::eval_error("Unexpected `break` statement outside of a loop"); } + } }; template struct Reference_AST_Node final : AST_Node_Impl { - Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 1); } + Reference_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Reference, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 1); + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - Boxed_Value bv; - t_ss.add_object(this->children[0]->text, bv); - return bv; - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + Boxed_Value bv; + t_ss.add_object(this->children[0]->text, bv); + return bv; + } }; template struct Prefix_AST_Node final : AST_Node_Impl { - Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)), - m_oper(Operators::to_operator(this->text, true)) - { } + Prefix_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Prefix, std::move(t_loc), std::move(t_children)) + , m_oper(Operators::to_operator(this->text, true)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - Boxed_Value bv(this->children[0]->eval(t_ss)); + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + Boxed_Value bv(this->children[0]->eval(t_ss)); - try { - // short circuit arithmetic operations - if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) - { - if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const()) - { - throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value."); - } - return Boxed_Number::do_oper(m_oper, bv); - } else { - chaiscript::eval::detail::Function_Push_Pop fpp(t_ss); - fpp.save_params({bv}); - return t_ss->call_function(this->text, m_loc, {std::move(bv)}, t_ss.conversions()); + try { + // short circuit arithmetic operations + if (m_oper != Operators::Opers::invalid && m_oper != Operators::Opers::bitwise_and && bv.get_type_info().is_arithmetic()) { + if ((m_oper == Operators::Opers::pre_increment || m_oper == Operators::Opers::pre_decrement) && bv.is_const()) { + throw exception::eval_error("Error with prefix operator evaluation: cannot modify constant value."); } - } catch (const exception::dispatch_error &e) { - throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss); + 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()); } + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Error with prefix operator evaluation: '" + this->text + "'", e.parameters, e.functions, false, *t_ss); } + } - private: - Operators::Opers m_oper = Operators::Opers::invalid; - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + Operators::Opers m_oper = Operators::Opers::invalid; + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Break_AST_Node final : AST_Node_Impl { - Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { } + Break_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Break, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ - throw detail::Break_Loop(); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Break_Loop(); } }; template struct Continue_AST_Node final : AST_Node_Impl { - Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { } + Continue_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Continue, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ - throw detail::Continue_Loop(); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { throw detail::Continue_Loop(); } }; template struct Noop_AST_Node final : AST_Node_Impl { - Noop_AST_Node() : - AST_Node_Impl("", AST_Node_Type::Noop, Parse_Location()) - { } + Noop_AST_Node() + : AST_Node_Impl("", AST_Node_Type::Noop, Parse_Location()) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override{ - // It's a no-op, that evaluates to "void" - return val; - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &) const override { + // It's a no-op, that evaluates to "void" + return val; + } - Boxed_Value val = void_var(); + Boxed_Value val = void_var(); }; template struct Map_Pair_AST_Node final : AST_Node_Impl { - Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { } + Map_Pair_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Map_Pair, std::move(t_loc), std::move(t_children)) { + } }; template struct Value_Range_AST_Node final : AST_Node_Impl { - Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { } + Value_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Value_Range, std::move(t_loc), std::move(t_children)) { + } }; template struct Inline_Range_AST_Node final : AST_Node_Impl { - Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { } + Inline_Range_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Inline_Range, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ - try { - auto oper1 = this->children[0]->children[0]->children[0]->eval(t_ss); - auto oper2 = this->children[0]->children[0]->children[1]->eval(t_ss); - return t_ss->call_function("generate_range", m_loc, {oper1, oper2}, t_ss.conversions()); - } - catch (const exception::dispatch_error &e) { - throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + try { + std::array params{this->children[0]->children[0]->children[0]->eval(t_ss), + this->children[0]->children[0]->children[1]->eval(t_ss)}; + + return t_ss->call_function("generate_range", m_loc, Function_Params{params}, t_ss.conversions()); + } catch (const exception::dispatch_error &e) { + throw exception::eval_error("Unable to generate range vector, while calling 'generate_range'", e.parameters, e.functions, false, *t_ss); } + } - private: - mutable std::atomic_uint_fast32_t m_loc = {0}; + private: + mutable std::atomic_uint_fast32_t m_loc = {0}; }; template struct Try_AST_Node final : AST_Node_Impl { - Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { } + Try_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Try, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const - { - Boxed_Value retval; + Boxed_Value handle_exception(const chaiscript::detail::Dispatch_State &t_ss, const Boxed_Value &t_except) const { + Boxed_Value retval; - size_t end_point = this->children.size(); - if (this->children.back()->identifier == AST_Node_Type::Finally) { - assert(end_point > 0); - end_point = this->children.size() - 1; - } - for (size_t i = 1; i < end_point; ++i) { - chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); - auto &catch_block = *this->children[i]; - - if (catch_block.children.size() == 1) { - //No variable capture, no guards - retval = catch_block.children[0]->eval(t_ss); - break; - } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) { - const auto name = Arg_List_AST_Node::get_arg_name(*catch_block.children[0]); - - if (dispatch::Param_Types( - std::vector>{Arg_List_AST_Node::get_arg_type(*catch_block.children[0], t_ss)} - ).match(std::vector{t_except}, t_ss.conversions()).first) - { - t_ss.add_object(name, t_except); - - if (catch_block.children.size() == 2) { - //Variable capture, no guards - retval = catch_block.children[1]->eval(t_ss); - break; - } - else if (catch_block.children.size() == 3) { - //Variable capture, guards - - bool guard = false; - try { - guard = boxed_cast(catch_block.children[1]->eval(t_ss)); - } catch (const exception::bad_boxed_cast &) { - if (this->children.back()->identifier == AST_Node_Type::Finally) { - this->children.back()->children[0]->eval(t_ss); - } - throw exception::eval_error("Guard condition not boolean"); - } - if (guard) { - retval = catch_block.children[2]->eval(t_ss); - break; - } - } - } - } - else { - if (this->children.back()->identifier == AST_Node_Type::Finally) { - this->children.back()->children[0]->eval(t_ss); - } - throw exception::eval_error("Internal error: catch block size unrecognized"); - } - } - - return retval; + size_t end_point = this->children.size(); + if (this->children.back()->identifier == AST_Node_Type::Finally) { + assert(end_point > 0); + end_point = this->children.size() - 1; } + for (size_t i = 1; i < end_point; ++i) { + chaiscript::eval::detail::Scope_Push_Pop catch_scope(t_ss); + auto &catch_block = *this->children[i]; - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { - Boxed_Value retval; + if (catch_block.children.size() == 1) { + // No variable capture + retval = catch_block.children[0]->eval(t_ss); + break; + } else if (catch_block.children.size() == 2 || catch_block.children.size() == 3) { + const auto name = Arg_List_AST_Node::get_arg_name(*catch_block.children[0]); - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + 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()) + .first) { + t_ss.add_object(name, t_except); - - try { - retval = this->children[0]->eval(t_ss); - } - catch (const exception::eval_error &e) { - retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); - } - catch (const std::runtime_error &e) { - retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); - } - catch (const std::out_of_range &e) { - retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); - } - catch (const std::exception &e) { - retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); - } - catch (Boxed_Value &e) { - retval = handle_exception(t_ss, e); - } - catch (...) { + if (catch_block.children.size() == 2) { + // Variable capture + retval = catch_block.children[1]->eval(t_ss); + break; + } + } + } else { if (this->children.back()->identifier == AST_Node_Type::Finally) { this->children.back()->children[0]->eval(t_ss); } - throw; + throw exception::eval_error("Internal error: catch block size unrecognized"); } - - - if (this->children.back()->identifier == AST_Node_Type::Finally) { - retval = this->children.back()->children[0]->eval(t_ss); - } - - return retval; } + return retval; + } + + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + Boxed_Value retval; + + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); + + try { + retval = this->children[0]->eval(t_ss); + } catch (const exception::eval_error &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::runtime_error &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::out_of_range &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (const std::exception &e) { + retval = handle_exception(t_ss, Boxed_Value(std::ref(e))); + } catch (Boxed_Value &e) { + retval = handle_exception(t_ss, e); + } catch (...) { + if (this->children.back()->identifier == AST_Node_Type::Finally) { + this->children.back()->children[0]->eval(t_ss); + } + throw; + } + + if (this->children.back()->identifier == AST_Node_Type::Finally) { + retval = this->children.back()->children[0]->eval(t_ss); + } + + return retval; + } }; template struct Catch_AST_Node final : AST_Node_Impl { - Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { } + Catch_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Catch, std::move(t_loc), std::move(t_children)) { + } }; template struct Finally_AST_Node final : AST_Node_Impl { - Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { } + Finally_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Finally, std::move(t_loc), std::move(t_children)) { + } }; template struct Method_AST_Node final : AST_Node_Impl { - std::shared_ptr> m_body_node; - std::shared_ptr> m_guard_node; + std::shared_ptr> m_body_node; + std::shared_ptr> m_guard_node; - Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Method, std::move(t_loc), - std::vector>(std::make_move_iterator(t_children.begin()), - std::make_move_iterator(std::prev(t_children.end(), Def_AST_Node::has_guard(t_children, 1)?2:1))) - ), - m_body_node(Def_AST_Node::get_body_node(std::move(t_children))), - m_guard_node(Def_AST_Node::get_guard_node(std::move(t_children), t_children.size()-this->children.size()==2)) - { - } + Method_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), + AST_Node_Type::Method, + std::move(t_loc), + std::vector>(std::make_move_iterator(t_children.begin()), + std::make_move_iterator( + std::prev(t_children.end(), Def_AST_Node::has_guard(t_children, 1) ? 2 : 1)))) + , m_body_node(Def_AST_Node::get_body_node(std::move(t_children))) + , m_guard_node(Def_AST_Node::get_guard_node(std::move(t_children), t_children.size() - this->children.size() == 2)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override{ + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + AST_Node_Impl_Ptr guardnode; - AST_Node_Impl_Ptr guardnode; + const std::string &class_name = this->children[0]->text; - const std::string & class_name = this->children[0]->text; + // The first param of a method is always the implied this ptr. + std::vector t_param_names{"this"}; + dispatch::Param_Types param_types; - //The first param of a method is always the implied this ptr. - std::vector t_param_names{"this"}; - dispatch::Param_Types param_types; - - if ((this->children.size() > 2) - && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { - auto args = Arg_List_AST_Node::get_arg_names(*this->children[2]); - t_param_names.insert(t_param_names.end(), args.begin(), args.end()); - param_types = Arg_List_AST_Node::get_arg_types(*this->children[2], t_ss); - } - - const size_t numparams = t_param_names.size(); - - std::shared_ptr guard; - std::reference_wrapper engine(*t_ss); - if (m_guard_node) { - guard = dispatch::make_dynamic_proxy_function( - [engine, t_param_names, guardnode = m_guard_node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params); - }, - static_cast(numparams), m_guard_node); - } - - try { - const std::string & function_name = this->children[1]->text; - - if (function_name == class_name) { - param_types.push_front(class_name, Type_Info()); - - t_ss->add( - std::make_shared(class_name, - dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node = m_body_node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); - }, - static_cast(numparams), m_body_node, param_types, guard - ) - ), - function_name); - - } else { - // if the type is unknown, then this generates a function that looks up the type - // at runtime. Defining the type first before this is called is better - auto type = t_ss->get_type(class_name, false); - param_types.push_front(class_name, type); - - t_ss->add( - std::make_shared(class_name, - dispatch::make_dynamic_proxy_function( - [engine, t_param_names, node = m_body_node](const std::vector &t_params) { - return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); - }, - static_cast(numparams), m_body_node, param_types, guard), type), - function_name); - } - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Method redefined '" + e.name() + "'"); - } - return void_var(); + if ((this->children.size() > 2) && (this->children[2]->identifier == AST_Node_Type::Arg_List)) { + auto args = Arg_List_AST_Node::get_arg_names(*this->children[2]); + t_param_names.insert(t_param_names.end(), args.begin(), args.end()); + param_types = Arg_List_AST_Node::get_arg_types(*this->children[2], t_ss); } + const size_t numparams = t_param_names.size(); + + std::shared_ptr guard; + std::reference_wrapper engine(*t_ss); + if (m_guard_node) { + guard = dispatch::make_dynamic_proxy_function( + [engine, t_param_names, guardnode = m_guard_node](const Function_Params &t_params) { + return chaiscript::eval::detail::eval_function(engine, *guardnode, t_param_names, t_params); + }, + static_cast(numparams), + m_guard_node); + } + + try { + const std::string &function_name = this->children[1]->text; + + if (function_name == class_name) { + param_types.push_front(class_name, Type_Info()); + + t_ss->add(std::make_shared( + class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node = m_body_node](const Function_Params &t_params) { + return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); + }, + static_cast(numparams), + m_body_node, + param_types, + guard)), + function_name); + + } else { + // if the type is unknown, then this generates a function that looks up the type + // at runtime. Defining the type first before this is called is better + auto type = t_ss->get_type(class_name, false); + param_types.push_front(class_name, type); + + t_ss->add(std::make_shared( + class_name, + dispatch::make_dynamic_proxy_function( + [engine, t_param_names, node = m_body_node](const Function_Params &t_params) { + return chaiscript::eval::detail::eval_function(engine, *node, t_param_names, t_params); + }, + static_cast(numparams), + m_body_node, + param_types, + guard), + type), + function_name); + } + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Method redefined '" + e.name() + "'"); + } + return void_var(); + } }; template struct Attr_Decl_AST_Node final : AST_Node_Impl { - Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { } + Attr_Decl_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Attr_Decl, std::move(t_loc), std::move(t_children)) { + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - std::string class_name = this->children[0]->text; + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + std::string class_name = this->children[0]->text; - try { - std::string attr_name = this->children[1]->text; + try { + std::string attr_name = this->children[1]->text; - t_ss->add( - std::make_shared( - std::move(class_name), - fun([attr_name](dispatch::Dynamic_Object &t_obj) { - return t_obj.get_attr(attr_name); - }), - true + t_ss->add(std::make_shared(std::move(class_name), + fun([attr_name](dispatch::Dynamic_Object &t_obj) { + return t_obj.get_attr(attr_name); + }), + true - ), this->children[1]->text); - } catch (const exception::name_conflict_error &e) { - throw exception::eval_error("Attribute redefined '" + e.name() + "'"); - } - return void_var(); + ), + this->children[1]->text); + } catch (const exception::name_conflict_error &e) { + throw exception::eval_error("Attribute redefined '" + e.name() + "'"); } - + return void_var(); + } }; - template struct Logical_And_AST_Node final : AST_Node_Impl { - Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 2); } - - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) - && this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); - } + Logical_And_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_And, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 2); + } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) + && this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); + } }; template struct Logical_Or_AST_Node final : AST_Node_Impl { - Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) : - AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) - { assert(this->children.size() == 2); } + Logical_Or_AST_Node(std::string t_ast_node_text, Parse_Location t_loc, std::vector> t_children) + : AST_Node_Impl(std::move(t_ast_node_text), AST_Node_Type::Logical_Or, std::move(t_loc), std::move(t_children)) { + assert(this->children.size() == 2); + } - Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override - { - return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) - || this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); - } + Boxed_Value eval_internal(const chaiscript::detail::Dispatch_State &t_ss) const override { + return const_var(this->get_bool_condition(this->children[0]->eval(t_ss), t_ss) + || this->get_bool_condition(this->children[1]->eval(t_ss), t_ss)); + } }; - } + } // namespace eval - -} +} // namespace chaiscript #endif /* CHAISCRIPT_EVAL_HPP_ */ - diff --git a/include/chaiscript/language/chaiscript_optimizer.hpp b/include/chaiscript/language/chaiscript_optimizer.hpp index 4bcae1a4..d9f706b6 100644 --- a/include/chaiscript/language/chaiscript_optimizer.hpp +++ b/include/chaiscript/language/chaiscript_optimizer.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_OPTIMIZER_HPP_ @@ -9,76 +9,68 @@ #include "chaiscript_eval.hpp" - namespace chaiscript { namespace optimizer { - - template - struct Optimizer : T... - { + template + struct Optimizer : T... { Optimizer() = default; - explicit Optimizer(T ... t) - : T(std::move(t))... - { + explicit Optimizer(T... t) + : T(std::move(t))... { } template auto optimize(eval::AST_Node_Impl_Ptr p) { - (void)std::initializer_list{ (p = static_cast(*this).optimize(std::move(p)), 0)... }; + ((p = static_cast(*this).optimize(std::move(p))), ...); return p; } }; template - eval::AST_Node_Impl &child_at(eval::AST_Node_Impl &node, const size_t offset) { - if (node.children[offset]->identifier == AST_Node_Type::Compiled) { - return *(dynamic_cast &>(*node.children[offset]).m_original_node); - } else { - return *node.children[offset]; - } + eval::AST_Node_Impl &child_at(eval::AST_Node_Impl &node, const size_t offset) noexcept { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *(dynamic_cast &>(*node.children[offset]).m_original_node); + } else { + return *node.children[offset]; } + } template - const eval::AST_Node_Impl &child_at(const eval::AST_Node_Impl &node, const size_t offset) { - if (node.children[offset]->identifier == AST_Node_Type::Compiled) { - return *(dynamic_cast &>(*node.children[offset]).m_original_node); - } else { - return *node.children[offset]; - } - - - /* - if (node->identifier == AST_Node_Type::Compiled) { - return dynamic_cast&>(*node).m_original_node->children[offset]; - } else { - return node->children[offset]; - } - */ + const eval::AST_Node_Impl &child_at(const eval::AST_Node_Impl &node, const size_t offset) noexcept { + if (node.children[offset]->identifier == AST_Node_Type::Compiled) { + return *(dynamic_cast &>(*node.children[offset]).m_original_node); + } else { + return *node.children[offset]; } + /* + if (node->identifier == AST_Node_Type::Compiled) { + return dynamic_cast&>(*node).m_original_node->children[offset]; + } else { + return node->children[offset]; + } + */ + } + template - auto child_count(const eval::AST_Node_Impl &node) { - if (node.identifier == AST_Node_Type::Compiled) { - return dynamic_cast&>(node).m_original_node->children.size(); - } else { - return node.children.size(); - } + auto child_count(const eval::AST_Node_Impl &node) noexcept { + if (node.identifier == AST_Node_Type::Compiled) { + return dynamic_cast &>(node).m_original_node->children.size(); + } else { + return node.children.size(); } + } template - auto make_compiled_node(eval::AST_Node_Impl_Ptr original_node, std::vector> children, Callable callable) - { - return chaiscript::make_unique, eval::Compiled_AST_Node>(std::move(original_node), std::move(children), std::move(callable)); - } - + auto make_compiled_node(eval::AST_Node_Impl_Ptr original_node, std::vector> children, Callable callable) { + return chaiscript::make_unique, eval::Compiled_AST_Node>(std::move(original_node), + std::move(children), + std::move(callable)); + } struct Return { template - auto optimize(eval::AST_Node_Impl_Ptr p) - { - if ( (p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) - && !p->children.empty()) - { + auto optimize(eval::AST_Node_Impl_Ptr p) { + if ((p->identifier == AST_Node_Type::Def || p->identifier == AST_Node_Type::Lambda) && !p->children.empty()) { auto &last_child = p->children.back(); if (last_child->identifier == AST_Node_Type::Block) { auto &block_last_child = last_child->children.back(); @@ -95,9 +87,9 @@ namespace chaiscript { }; template - bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) - { - if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl || node.identifier == AST_Node_Type::Reference) { + bool contains_var_decl_in_scope(const eval::AST_Node_Impl &node) noexcept { + if (node.identifier == AST_Node_Type::Var_Decl || node.identifier == AST_Node_Type::Assign_Decl + || node.identifier == AST_Node_Type::Reference) { return true; } @@ -105,10 +97,8 @@ namespace chaiscript { for (size_t i = 0; i < num; ++i) { const auto &child = child_at(node, i); - if (child.identifier != AST_Node_Type::Block - && child.identifier != AST_Node_Type::For - && child.identifier != AST_Node_Type::Ranged_For - && contains_var_decl_in_scope(child)) { + if (child.identifier != AST_Node_Type::Block && child.identifier != AST_Node_Type::For + && child.identifier != AST_Node_Type::Ranged_For && contains_var_decl_in_scope(child)) { return true; } } @@ -119,15 +109,14 @@ namespace chaiscript { struct Block { template auto optimize(eval::AST_Node_Impl_Ptr node) { - if (node->identifier == AST_Node_Type::Block) - { - if (!contains_var_decl_in_scope(*node)) - { + if (node->identifier == AST_Node_Type::Block) { + if (!contains_var_decl_in_scope(*node)) { if (node->children.size() == 1) { return std::move(node->children[0]); } else { - return chaiscript::make_unique, eval::Scopeless_Block_AST_Node>(node->text, node->location, - std::move(node->children)); + return chaiscript::make_unique, eval::Scopeless_Block_AST_Node>(node->text, + node->location, + std::move(node->children)); } } } @@ -138,105 +127,91 @@ namespace chaiscript { struct Dead_Code { template - auto optimize(eval::AST_Node_Impl_Ptr node) { - if (node->identifier == AST_Node_Type::Block) - { - std::vector keepers; - const auto num_children = node->children.size(); - keepers.reserve(num_children); + auto optimize(eval::AST_Node_Impl_Ptr node) { + if (node->identifier == AST_Node_Type::Block) { + std::vector keepers; + const auto num_children = node->children.size(); + keepers.reserve(num_children); - for (size_t i = 0; i < num_children; ++i) { - const auto &child = *node->children[i]; - if ( (child.identifier != AST_Node_Type::Id - && child.identifier != AST_Node_Type::Constant - && child.identifier != AST_Node_Type::Noop) - || i == num_children - 1) { - keepers.push_back(i); - } + for (size_t i = 0; i < num_children; ++i) { + const auto &child = *node->children[i]; + if ((child.identifier != AST_Node_Type::Id && child.identifier != AST_Node_Type::Constant + && child.identifier != AST_Node_Type::Noop) + || i == num_children - 1) { + keepers.push_back(i); } - - if (keepers.size() == num_children) { - return node; - } else { - const auto new_children = [&](){ - std::vector> retval; - for (const auto x : keepers) - { - retval.push_back(std::move(node->children[x])); - } - return retval; - }; - - return chaiscript::make_unique, eval::Block_AST_Node>(node->text, node->location, new_children()); - } - } else { - return node; } + + if (keepers.size() == num_children) { + return node; + } else { + const auto new_children = [&]() { + std::vector> retval; + for (const auto x : keepers) { + retval.push_back(std::move(node->children[x])); + } + return retval; + }; + + return chaiscript::make_unique, eval::Block_AST_Node>(node->text, node->location, new_children()); + } + } else { + return node; } + } }; struct Unused_Return { template - auto optimize(eval::AST_Node_Impl_Ptr node) { - if ((node->identifier == AST_Node_Type::Block - || node->identifier == AST_Node_Type::Scopeless_Block) - && !node->children.empty()) - { - for (size_t i = 0; i < node->children.size()-1; ++i) { - auto child = node->children[i].get(); - if (child->identifier == AST_Node_Type::Fun_Call) { - node->children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(child->text, child->location, - std::move(child->children)); - } + auto optimize(eval::AST_Node_Impl_Ptr node) { + if ((node->identifier == AST_Node_Type::Block || node->identifier == AST_Node_Type::Scopeless_Block) && !node->children.empty()) { + for (size_t i = 0; i < node->children.size() - 1; ++i) { + auto child = node->children[i].get(); + if (child->identifier == AST_Node_Type::Fun_Call) { + node->children[i] + = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(child->text, + child->location, + std::move(child->children)); } - } else if ((node->identifier == AST_Node_Type::For - || node->identifier == AST_Node_Type::While) - && child_count(*node) > 0) { - auto &child = child_at(*node, child_count(*node) - 1); - if (child.identifier == AST_Node_Type::Block - || child.identifier == AST_Node_Type::Scopeless_Block) - { - auto num_sub_children = child_count(child); - for (size_t i = 0; i < num_sub_children; ++i) { - auto &sub_child = child_at(child, i); - if (sub_child.identifier == AST_Node_Type::Fun_Call) { - child.children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>(sub_child.text, sub_child.location, std::move(sub_child.children)); - } + } + } else if ((node->identifier == AST_Node_Type::For || node->identifier == AST_Node_Type::While) && child_count(*node) > 0) { + auto &child = child_at(*node, child_count(*node) - 1); + if (child.identifier == AST_Node_Type::Block || child.identifier == AST_Node_Type::Scopeless_Block) { + auto num_sub_children = child_count(child); + for (size_t i = 0; i < num_sub_children; ++i) { + auto &sub_child = child_at(child, i); + if (sub_child.identifier == AST_Node_Type::Fun_Call) { + child.children[i] = chaiscript::make_unique, eval::Unused_Return_Fun_Call_AST_Node>( + sub_child.text, sub_child.location, std::move(sub_child.children)); } } } - return node; } + return node; + } }; struct Assign_Decl { template auto optimize(eval::AST_Node_Impl_Ptr node) { - if ((node->identifier == AST_Node_Type::Equation) - && node->text == "=" - && node->children.size() == 2 - && node->children[0]->identifier == AST_Node_Type::Var_Decl - ) - { + if ((node->identifier == AST_Node_Type::Equation) && node->text == "=" && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Var_Decl) { std::vector> new_children; new_children.push_back(std::move(node->children[0]->children[0])); new_children.push_back(std::move(node->children[1])); - return chaiscript::make_unique, eval::Assign_Decl_AST_Node>(node->text, - node->location, std::move(new_children) ); + return chaiscript::make_unique, eval::Assign_Decl_AST_Node>(node->text, + node->location, + std::move(new_children)); } return node; } }; - struct If { template auto optimize(eval::AST_Node_Impl_Ptr node) { - if ((node->identifier == AST_Node_Type::If) - && node->children.size() >= 2 - && node->children[0]->identifier == AST_Node_Type::Constant) - { + if ((node->identifier == AST_Node_Type::If) && node->children.size() >= 2 && node->children[0]->identifier == AST_Node_Type::Constant) { const auto condition = dynamic_cast *>(node->children[0].get())->m_value; if (condition.get_type_info().bare_equal_type_info(typeid(bool))) { if (boxed_cast(condition)) { @@ -254,25 +229,21 @@ namespace chaiscript { struct Partial_Fold { template auto optimize(eval::AST_Node_Impl_Ptr node) { - // Fold right side - if (node->identifier == AST_Node_Type::Binary - && node->children.size() == 2 - && node->children[0]->identifier != AST_Node_Type::Constant - && node->children[1]->identifier == AST_Node_Type::Constant) - { + if (node->identifier == AST_Node_Type::Binary && node->children.size() == 2 + && node->children[0]->identifier != AST_Node_Type::Constant && node->children[1]->identifier == AST_Node_Type::Constant) { try { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper); if (parsed != Operators::Opers::invalid) { const auto rhs = dynamic_cast *>(node->children[1].get())->m_value; if (rhs.get_type_info().is_arithmetic()) { - return chaiscript::make_unique, eval::Fold_Right_Binary_Operator_AST_Node>(node->text, node->location, - std::move(node->children), rhs); + return chaiscript::make_unique, eval::Fold_Right_Binary_Operator_AST_Node>( + node->text, node->location, std::move(node->children), rhs); } } } catch (const std::exception &) { - //failure to fold, that's OK + // failure to fold, that's OK } } @@ -283,11 +254,7 @@ namespace chaiscript { struct Constant_Fold { template auto optimize(eval::AST_Node_Impl_Ptr node) { - - if (node->identifier == AST_Node_Type::Prefix - && node->children.size() == 1 - && node->children[0]->identifier == AST_Node_Type::Constant) - { + if (node->identifier == AST_Node_Type::Prefix && node->children.size() == 1 && node->children[0]->identifier == AST_Node_Type::Constant) { try { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper, true); @@ -295,39 +262,43 @@ namespace chaiscript { const auto match = oper + node->children[0]->text; if (parsed != Operators::Opers::invalid && parsed != Operators::Opers::bitwise_and && lhs.get_type_info().is_arithmetic()) { - const auto val = Boxed_Number::do_oper(parsed, lhs); - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + const auto val = Boxed_Number::do_oper(parsed, lhs); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), + node->location, + std::move(val)); } else if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && oper == "!") { - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, Boxed_Value(!boxed_cast(lhs))); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), + node->location, + Boxed_Value(!boxed_cast(lhs))); } } catch (const std::exception &) { - //failure to fold, that's OK + // failure to fold, that's OK } } else if ((node->identifier == AST_Node_Type::Logical_And || node->identifier == AST_Node_Type::Logical_Or) - && node->children.size() == 2 - && node->children[0]->identifier == AST_Node_Type::Constant - && node->children[1]->identifier == AST_Node_Type::Constant) - { + && node->children.size() == 2 && node->children[0]->identifier == AST_Node_Type::Constant + && node->children[1]->identifier == AST_Node_Type::Constant) { try { const auto lhs = dynamic_cast &>(*node->children[0]).m_value; const auto rhs = dynamic_cast &>(*node->children[1]).m_value; if (lhs.get_type_info().bare_equal_type_info(typeid(bool)) && rhs.get_type_info().bare_equal_type_info(typeid(bool))) { const auto match = node->children[0]->text + " " + node->text + " " + node->children[1]->text; const auto val = [lhs_val = boxed_cast(lhs), rhs_val = boxed_cast(rhs), id = node->identifier] { - if (id == AST_Node_Type::Logical_And) { return Boxed_Value(lhs_val && rhs_val); } - else { return Boxed_Value(lhs_val || rhs_val); } + if (id == AST_Node_Type::Logical_And) { + return Boxed_Value(lhs_val && rhs_val); + } else { + return Boxed_Value(lhs_val || rhs_val); + } }(); - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), + node->location, + std::move(val)); } } catch (const std::exception &) { - //failure to fold, that's OK + // failure to fold, that's OK } - } else if (node->identifier == AST_Node_Type::Binary - && node->children.size() == 2 - && node->children[0]->identifier == AST_Node_Type::Constant - && node->children[1]->identifier == AST_Node_Type::Constant) - { + } else if (node->identifier == AST_Node_Type::Binary && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Constant && node->children[1]->identifier == AST_Node_Type::Constant) { try { const auto &oper = node->text; const auto parsed = Operators::to_operator(oper); @@ -335,28 +306,28 @@ namespace chaiscript { const auto lhs = dynamic_cast &>(*node->children[0]).m_value; const auto rhs = dynamic_cast &>(*node->children[1]).m_value; if (lhs.get_type_info().is_arithmetic() && rhs.get_type_info().is_arithmetic()) { - const auto val = Boxed_Number::do_oper(parsed, lhs, rhs); + const auto val = Boxed_Number::do_oper(parsed, lhs, rhs); const auto match = node->children[0]->text + " " + oper + " " + node->children[1]->text; - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, std::move(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), + node->location, + std::move(val)); } } } catch (const std::exception &) { - //failure to fold, that's OK + // failure to fold, that's OK } - } else if (node->identifier == AST_Node_Type::Fun_Call - && node->children.size() == 2 - && node->children[0]->identifier == AST_Node_Type::Id - && node->children[1]->identifier == AST_Node_Type::Arg_List - && node->children[1]->children.size() == 1 - && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) { - + } else if (node->identifier == AST_Node_Type::Fun_Call && node->children.size() == 2 + && node->children[0]->identifier == AST_Node_Type::Id && node->children[1]->identifier == AST_Node_Type::Arg_List + && node->children[1]->children.size() == 1 && node->children[1]->children[0]->identifier == AST_Node_Type::Constant) { const auto arg = dynamic_cast &>(*node->children[1]->children[0]).m_value; if (arg.get_type_info().is_arithmetic()) { const auto &fun_name = node->children[0]->text; - const auto make_constant = [&node, &fun_name](auto val){ + const auto make_constant = [&node, &fun_name](auto val) { const auto match = fun_name + "(" + node->children[1]->children[0]->text + ")"; - return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), node->location, const_var(val)); + return chaiscript::make_unique, eval::Constant_AST_Node>(std::move(match), + node->location, + const_var(val)); }; if (fun_name == "double") { @@ -370,10 +341,7 @@ namespace chaiscript { } else if (fun_name == "size_t") { return make_constant(Boxed_Number(arg).get_as()); } - - } - } return node; @@ -383,7 +351,6 @@ namespace chaiscript { struct For_Loop { template auto optimize(eval::AST_Node_Impl_Ptr for_node) { - if (for_node->identifier != AST_Node_Type::For) { return for_node; } @@ -392,66 +359,55 @@ namespace chaiscript { const auto &binary_node = child_at(*for_node, 1); const auto &prefix_node = child_at(*for_node, 2); - if (child_count(*for_node) == 4 - && eq_node.identifier == AST_Node_Type::Assign_Decl - && child_count(eq_node) == 2 - && child_at(eq_node, 0).identifier == AST_Node_Type::Id - && child_at(eq_node, 1).identifier == AST_Node_Type::Constant - && binary_node.identifier == AST_Node_Type::Binary - && binary_node.text == "<" - && child_count(binary_node) == 2 - && child_at(binary_node, 0).identifier == AST_Node_Type::Id - && child_at(binary_node, 0).text == child_at(eq_node,0).text - && child_at(binary_node, 1).identifier == AST_Node_Type::Constant - && prefix_node.identifier == AST_Node_Type::Prefix - && prefix_node.text == "++" - && child_count(prefix_node) == 1 - && child_at(prefix_node, 0).identifier == AST_Node_Type::Id - && child_at(prefix_node, 0).text == child_at(eq_node,0).text) - { + if (child_count(*for_node) == 4 && eq_node.identifier == AST_Node_Type::Assign_Decl && child_count(eq_node) == 2 + && child_at(eq_node, 0).identifier == AST_Node_Type::Id && child_at(eq_node, 1).identifier == AST_Node_Type::Constant + && binary_node.identifier == AST_Node_Type::Binary && binary_node.text == "<" && child_count(binary_node) == 2 + && child_at(binary_node, 0).identifier == AST_Node_Type::Id && child_at(binary_node, 0).text == child_at(eq_node, 0).text + && child_at(binary_node, 1).identifier == AST_Node_Type::Constant && prefix_node.identifier == AST_Node_Type::Prefix + && prefix_node.text == "++" && child_count(prefix_node) == 1 && child_at(prefix_node, 0).identifier == AST_Node_Type::Id + && child_at(prefix_node, 0).text == child_at(eq_node, 0).text) { const Boxed_Value &begin = dynamic_cast &>(child_at(eq_node, 1)).m_value; const Boxed_Value &end = dynamic_cast &>(child_at(binary_node, 1)).m_value; const std::string &id = child_at(prefix_node, 0).text; - if (begin.get_type_info().bare_equal(user_type()) - && end.get_type_info().bare_equal(user_type())) { - + if (begin.get_type_info().bare_equal(user_type()) && end.get_type_info().bare_equal(user_type())) { const auto start_int = boxed_cast(begin); const auto end_int = boxed_cast(end); - // note that we are moving the last element out, then popping the empty shared_ptr + // note that we are moving the last element out, then popping the empty shared_ptr // from the vector std::vector> body_vector; auto body_child = std::move(for_node->children[3]); for_node->children.pop_back(); body_vector.emplace_back(std::move(body_child)); - - return make_compiled_node(std::move(for_node), std::move(body_vector), - [id, start_int, end_int](const std::vector> &children, const chaiscript::detail::Dispatch_State &t_ss) { - assert(children.size() == 1); - chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - int i = start_int; - t_ss.add_object(id, var(&i)); + return make_compiled_node(std::move(for_node), + std::move(body_vector), + [id, start_int, end_int](const std::vector> &children, + const chaiscript::detail::Dispatch_State &t_ss) { + assert(children.size() == 1); + chaiscript::eval::detail::Scope_Push_Pop spp(t_ss); - try { - for (; i < end_int; ++i) { - try { - // Body of Loop - children[0]->eval(t_ss); - } catch (eval::detail::Continue_Loop &) { - // we got a continue exception, which means all of the remaining - // loop implementation is skipped and we just need to continue to - // the next iteration step - } - } - } catch (eval::detail::Break_Loop &) { - // loop broken - } + int i = start_int; + t_ss.add_object(id, var(&i)); - return void_var(); - } - ); + try { + for (; i < end_int; ++i) { + try { + // Body of Loop + children[0]->eval(t_ss); + } catch (eval::detail::Continue_Loop &) { + // we got a continue exception, which means all of the remaining + // loop implementation is skipped and we just need to continue to + // the next iteration step + } + } + } catch (eval::detail::Break_Loop &) { + // loop broken + } + + return void_var(); + }); } else { return for_node; } @@ -461,11 +417,17 @@ namespace chaiscript { } }; - typedef Optimizer Optimizer_Default; - - } -} + using Optimizer_Default = Optimizer; + } // namespace optimizer +} // namespace chaiscript #endif diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 848296c9..df64f9b9 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1,38 +1,34 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_PARSER_HPP_ #define CHAISCRIPT_PARSER_HPP_ +#include +#include #include #include #include #include #include #include -#include -#include - - - #include "../dispatchkit/boxed_value.hpp" +#include "../utility/hash.hpp" +#include "../utility/static_string.hpp" #include "chaiscript_common.hpp" #include "chaiscript_optimizer.hpp" #include "chaiscript_tracer.hpp" -#include "../utility/fnv1a.hpp" -#include "../utility/static_string.hpp" #if defined(CHAISCRIPT_UTF16_UTF32) -#include #include +#include #endif #if defined(CHAISCRIPT_MSVC) && defined(max) && defined(min) @@ -43,57 +39,49 @@ #undef min #endif - -namespace chaiscript -{ +namespace chaiscript { /// \brief Classes and functions used during the parsing process. - namespace parser - { + namespace parser { /// \brief Classes and functions internal to the parsing process. Not supported for the end user. - namespace detail - { - enum Alphabet - { symbol_alphabet = 0 - , keyword_alphabet - , int_alphabet - , float_alphabet - , x_alphabet - , hex_alphabet - , b_alphabet - , bin_alphabet - , id_alphabet - , white_alphabet - , int_suffix_alphabet - , float_suffix_alphabet - , max_alphabet - , lengthof_alphabet = 256 + namespace detail { + enum Alphabet { + symbol_alphabet = 0, + keyword_alphabet, + int_alphabet, + float_alphabet, + x_alphabet, + hex_alphabet, + b_alphabet, + bin_alphabet, + id_alphabet, + white_alphabet, + int_suffix_alphabet, + float_suffix_alphabet, + max_alphabet, + lengthof_alphabet = 256 }; // Generic for u16, u32 and wchar template - struct Char_Parser_Helper - { + struct Char_Parser_Helper { // common for all implementations - static std::string u8str_from_ll(long long val) - { - typedef std::string::value_type char_type; + 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) - { + 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) - { - typedef typename string_type::value_type target_char_type; -#if defined (CHAISCRIPT_UTF16_UTF32) + 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 @@ -107,208 +95,267 @@ namespace chaiscript // Specialization for char AKA UTF-8 template<> - struct Char_Parser_Helper - { - static std::string str_from_ll(long long val) - { + 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); } }; - } + } // namespace detail - - template + template class ChaiScript_Parser final : public ChaiScript_Parser_Base { - void *get_tracer_ptr() override { - return &m_tracer; - } + void *get_tracer_ptr() noexcept override { return &m_tracer; } std::size_t m_current_parse_depth = 0; - struct Depth_Counter - { + struct Depth_Counter { static const auto max_depth = Parse_Depth; - Depth_Counter(ChaiScript_Parser *t_parser) : parser(t_parser) - { + Depth_Counter(ChaiScript_Parser *t_parser) + : parser(t_parser) { ++parser->m_current_parse_depth; if (parser->m_current_parse_depth > max_depth) { - throw exception::eval_error("Maximum parse depth exceeded", - File_Position(parser->m_position.line, parser->m_position.col), *(parser->m_filename)); + throw exception::eval_error("Maximum parse depth exceeded", + File_Position(parser->m_position.line, parser->m_position.col), + *(parser->m_filename)); } } - ~Depth_Counter() noexcept - { - --parser->m_current_parse_depth; - } + ~Depth_Counter() noexcept { --parser->m_current_parse_depth; } ChaiScript_Parser *parser; }; - static std::array, detail::max_alphabet> build_alphabet() - { - std::array, detail::max_alphabet> alphabet; + 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; + } - for (auto &alpha : alphabet) { - alpha.fill(false); + constexpr static std::array, detail::max_alphabet> build_alphabet() noexcept { + std::array, detail::max_alphabet> alphabet{}; + + set_alphabet(alphabet, detail::symbol_alphabet, '?'); + + set_alphabet(alphabet, detail::symbol_alphabet, '?'); + set_alphabet(alphabet, detail::symbol_alphabet, '+'); + set_alphabet(alphabet, detail::symbol_alphabet, '-'); + set_alphabet(alphabet, detail::symbol_alphabet, '*'); + set_alphabet(alphabet, detail::symbol_alphabet, '/'); + set_alphabet(alphabet, detail::symbol_alphabet, '|'); + set_alphabet(alphabet, detail::symbol_alphabet, '&'); + set_alphabet(alphabet, detail::symbol_alphabet, '^'); + set_alphabet(alphabet, detail::symbol_alphabet, '='); + set_alphabet(alphabet, detail::symbol_alphabet, '.'); + set_alphabet(alphabet, detail::symbol_alphabet, '<'); + set_alphabet(alphabet, detail::symbol_alphabet, '>'); + + for (size_t c = 'a'; c <= 'z'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + for (size_t c = 'A'; c <= 'Z'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::keyword_alphabet, c); + } + set_alphabet(alphabet, detail::keyword_alphabet, '_'); + + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::int_alphabet, c); + } + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::float_alphabet, c); + } + set_alphabet(alphabet, detail::float_alphabet, '.'); + + for (size_t c = '0'; c <= '9'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); + } + for (size_t c = 'a'; c <= 'f'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); + } + for (size_t c = 'A'; c <= 'F'; ++c) { + set_alphabet(alphabet, detail::hex_alphabet, c); } - alphabet[detail::symbol_alphabet][static_cast('?')]=true; - alphabet[detail::symbol_alphabet][static_cast('+')]=true; - alphabet[detail::symbol_alphabet][static_cast('-')]=true; - alphabet[detail::symbol_alphabet][static_cast('*')]=true; - alphabet[detail::symbol_alphabet][static_cast('/')]=true; - alphabet[detail::symbol_alphabet][static_cast('|')]=true; - alphabet[detail::symbol_alphabet][static_cast('&')]=true; - alphabet[detail::symbol_alphabet][static_cast('^')]=true; - alphabet[detail::symbol_alphabet][static_cast('=')]=true; - alphabet[detail::symbol_alphabet][static_cast('.')]=true; - alphabet[detail::symbol_alphabet][static_cast('<')]=true; - alphabet[detail::symbol_alphabet][static_cast('>')]=true; + set_alphabet(alphabet, detail::x_alphabet, 'x'); + set_alphabet(alphabet, detail::x_alphabet, 'X'); - for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; } - for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; } - for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::keyword_alphabet][c]=true; } - alphabet[detail::keyword_alphabet][static_cast('_')]=true; + for (size_t c = '0'; c <= '1'; ++c) { + set_alphabet(alphabet, detail::bin_alphabet, c); + } + set_alphabet(alphabet, detail::b_alphabet, 'b'); + set_alphabet(alphabet, detail::b_alphabet, 'B'); - for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::int_alphabet][c]=true; } - for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::float_alphabet][c]=true; } - alphabet[detail::float_alphabet][static_cast('.')]=true; + for (size_t c = 'a'; c <= 'z'; ++c) { + set_alphabet(alphabet, detail::id_alphabet, c); + } + for (size_t c = 'A'; c <= 'Z'; ++c) { + set_alphabet(alphabet, detail::id_alphabet, c); + } + set_alphabet(alphabet, detail::id_alphabet, '_'); - for ( size_t c = '0' ; c <= '9' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; } - for ( size_t c = 'a' ; c <= 'f' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; } - for ( size_t c = 'A' ; c <= 'F' ; ++c ) { alphabet[detail::hex_alphabet][c]=true; } + set_alphabet(alphabet, detail::white_alphabet, ' '); + set_alphabet(alphabet, detail::white_alphabet, '\t'); - alphabet[detail::x_alphabet][static_cast('x')]=true; - alphabet[detail::x_alphabet][static_cast('X')]=true; + set_alphabet(alphabet, detail::int_suffix_alphabet, 'l'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'L'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'u'); + set_alphabet(alphabet, detail::int_suffix_alphabet, 'U'); - for ( size_t c = '0' ; c <= '1' ; ++c ) { alphabet[detail::bin_alphabet][c]=true; } - alphabet[detail::b_alphabet][static_cast('b')]=true; - alphabet[detail::b_alphabet][static_cast('B')]=true; - - for ( size_t c = 'a' ; c <= 'z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; } - for ( size_t c = 'A' ; c <= 'Z' ; ++c ) { alphabet[detail::id_alphabet][c]=true; } - alphabet[detail::id_alphabet][static_cast('_')] = true; - - alphabet[detail::white_alphabet][static_cast(' ')]=true; - alphabet[detail::white_alphabet][static_cast('\t')]=true; - - alphabet[detail::int_suffix_alphabet][static_cast('l')] = true; - alphabet[detail::int_suffix_alphabet][static_cast('L')] = true; - alphabet[detail::int_suffix_alphabet][static_cast('u')] = true; - alphabet[detail::int_suffix_alphabet][static_cast('U')] = true; - - alphabet[detail::float_suffix_alphabet][static_cast('l')] = true; - alphabet[detail::float_suffix_alphabet][static_cast('L')] = true; - alphabet[detail::float_suffix_alphabet][static_cast('f')] = true; - alphabet[detail::float_suffix_alphabet][static_cast('F')] = true; + set_alphabet(alphabet, detail::float_suffix_alphabet, 'l'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'L'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'f'); + set_alphabet(alphabet, detail::float_suffix_alphabet, 'F'); return alphabet; } - static const std::array, detail::max_alphabet> &create_alphabet() - { - static const auto alpha = build_alphabet(); - return alpha; - } + struct Operator_Matches { + using SS = utility::Static_String; + std::array m_0{{SS("?")}}; + std::array m_1{{SS("||")}}; + std::array m_2{{SS("&&")}}; + std::array m_3{{SS("|")}}; + std::array m_4{{SS("^")}}; + std::array m_5{{SS("&")}}; + std::array m_6{{SS("=="), SS("!=")}}; + std::array m_7{{SS("<"), SS("<="), SS(">"), SS(">=")}}; + std::array m_8{{SS("<<"), SS(">>")}}; + // We share precedence here but then separate them later + std::array m_9{{SS("+"), SS("-")}}; + std::array m_10{{SS("*"), SS("/"), SS("%")}}; + std::array m_11{{SS("++"), SS("--"), SS("-"), SS("+"), SS("!"), SS("~")}}; - static const std::vector> &create_operator_matches() { - static const std::vector> operator_matches { - {"?"}, - {"||"}, - {"&&"}, - {"|"}, - {"^"}, - {"&"}, - {"==", "!="}, - {"<", "<=", ">", ">="}, - {"<<", ">>"}, - //We share precedence here but then separate them later - {"+", "-"}, - {"*", "/", "%"}, - {"++", "--", "-", "+", "!", "~"} - }; + bool is_match(std::string_view t_str) const noexcept { + constexpr std::array groups{{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}; + return std::any_of(groups.begin(), groups.end(), [&t_str, this](const std::size_t group) { return is_match(group, t_str); }); + } - return operator_matches; - } + template + bool any_of(const std::size_t t_group, Predicate &&predicate) const { + auto match = [&predicate](const auto &array) { return std::any_of(array.begin(), array.end(), predicate); }; + switch (t_group) { + case 0: + return match(m_0); + case 1: + return match(m_1); + case 2: + return match(m_2); + case 3: + return match(m_3); + case 4: + return match(m_4); + case 5: + return match(m_5); + case 6: + return match(m_6); + case 7: + return match(m_7); + case 8: + return match(m_8); + case 9: + return match(m_9); + case 10: + return match(m_10); + case 11: + return match(m_11); + default: + return false; + } + } - static const std::array &create_operators() { - static const std::array operators = { { - Operator_Precidence::Ternary_Cond, - Operator_Precidence::Logical_Or, - Operator_Precidence::Logical_And, - Operator_Precidence::Bitwise_Or, - Operator_Precidence::Bitwise_Xor, - Operator_Precidence::Bitwise_And, - Operator_Precidence::Equality, - Operator_Precidence::Comparison, - Operator_Precidence::Shift, - Operator_Precidence::Addition, - Operator_Precidence::Multiplication, - Operator_Precidence::Prefix - } }; + constexpr bool is_match(const std::size_t t_group, std::string_view t_str) const noexcept { + auto match = [&t_str](const auto &array) { + return std::any_of(array.begin(), array.end(), [&t_str](const auto &v) { return v == t_str; }); + }; + + switch (t_group) { + case 0: + return match(m_0); + case 1: + return match(m_1); + case 2: + return match(m_2); + case 3: + return match(m_3); + case 4: + return match(m_4); + case 5: + return match(m_5); + case 6: + return match(m_6); + case 7: + return match(m_7); + case 8: + return match(m_8); + case 9: + return match(m_9); + case 10: + return match(m_10); + case 11: + return match(m_11); + default: + return false; + } + } + }; + + constexpr static std::array create_operators() noexcept { + std::array operators = {{Operator_Precedence::Ternary_Cond, + Operator_Precedence::Logical_Or, + Operator_Precedence::Logical_And, + Operator_Precedence::Bitwise_Or, + Operator_Precedence::Bitwise_Xor, + Operator_Precedence::Bitwise_And, + Operator_Precedence::Equality, + Operator_Precedence::Comparison, + Operator_Precedence::Shift, + Operator_Precedence::Addition, + Operator_Precedence::Multiplication, + Operator_Precedence::Prefix}}; return operators; } - static const utility::Static_String &multiline_comment_end() - { - static const utility::Static_String s("*/"); - return s; - } - - static const utility::Static_String &multiline_comment_begin() - { - static const utility::Static_String s("/*"); - return s; - } - - static const utility::Static_String &singleline_comment() - { - static const utility::Static_String s("//"); - return s; - } - - static const utility::Static_String &annotation() - { - static const utility::Static_String s("#"); - return s; - } - - static const utility::Static_String &cr_lf() - { - static const utility::Static_String s("\r\n"); - return s; - } - - const std::array, detail::max_alphabet> &m_alphabet = create_alphabet(); - const std::vector> &m_operator_matches = create_operator_matches(); - const std::array &m_operators = create_operators(); + constexpr static utility::Static_String m_multiline_comment_end{"*/"}; + constexpr static utility::Static_String m_multiline_comment_begin{"/*"}; + constexpr static utility::Static_String m_singleline_comment{"//"}; + constexpr static utility::Static_String m_annotation{"#"}; + constexpr static utility::Static_String m_cr_lf{"\r\n"}; + constexpr static auto m_operators = create_operators(); std::shared_ptr m_filename; std::vector> m_match_stack; + struct Position { + constexpr Position() = default; - struct Position - { - Position() = default; - - Position(std::string::const_iterator t_pos, std::string::const_iterator t_end) - : line(1), col(1), m_pos(t_pos), m_end(t_end), m_last_col(1) - { + constexpr Position(const char *t_pos, const char *t_end) noexcept + : line(1) + , col(1) + , m_pos(t_pos) + , m_end(t_end) + , m_last_col(1) { } - static std::string str(const Position &t_begin, const Position &t_end) { - return std::string(t_begin.m_pos, t_end.m_pos); + static std::string_view str(const Position &t_begin, const Position &t_end) noexcept { + if (t_begin.m_pos != nullptr && t_end.m_pos != nullptr) { + return std::string_view(t_begin.m_pos, std::size_t(std::distance(t_begin.m_pos, t_end.m_pos))); + } else { + return {}; + } } - Position &operator++() { + constexpr Position &operator++() noexcept { if (m_pos != m_end) { if (*m_pos == '\n') { ++line; - m_last_col = std::exchange(col, 1); + m_last_col = col; + col = 1; } else { ++col; } @@ -318,7 +365,7 @@ namespace chaiscript return *this; } - Position &operator--() { + constexpr Position &operator--() noexcept { --m_pos; if (*m_pos == '\n') { --line; @@ -329,12 +376,12 @@ namespace chaiscript return *this; } - Position &operator+=(size_t t_distance) { + constexpr Position &operator+=(size_t t_distance) noexcept { *this = (*this) + t_distance; return *this; } - Position operator+(size_t t_distance) const { + constexpr Position operator+(size_t t_distance) const noexcept { Position ret(*this); for (size_t i = 0; i < t_distance; ++i) { ++ret; @@ -342,12 +389,12 @@ namespace chaiscript return ret; } - Position &operator-=(size_t t_distance) { + constexpr Position &operator-=(size_t t_distance) noexcept { *this = (*this) - t_distance; return *this; } - Position operator-(size_t t_distance) const { + constexpr Position operator-(size_t t_distance) const noexcept { Position ret(*this); for (size_t i = 0; i < t_distance; ++i) { --ret; @@ -355,26 +402,17 @@ namespace chaiscript return ret; } - bool operator==(const Position &t_rhs) const { - return m_pos == t_rhs.m_pos; - } + constexpr bool operator==(const Position &t_rhs) const noexcept { return m_pos == t_rhs.m_pos; } - bool operator!=(const Position &t_rhs) const { - return m_pos != t_rhs.m_pos; - } + constexpr bool operator!=(const Position &t_rhs) const noexcept { return m_pos != t_rhs.m_pos; } - bool has_more() const { - return m_pos != m_end; - } + constexpr bool has_more() const noexcept { return m_pos != m_end; } - size_t remaining() const { - return static_cast(std::distance(m_pos, m_end)); - } + constexpr size_t remaining() const noexcept { return static_cast(m_end - m_pos); } - const char& operator*() const { + constexpr const char &operator*() const noexcept { if (m_pos == m_end) { - static const char ktmp ='\0'; - return ktmp; + return ""[0]; } else { return *m_pos; } @@ -383,10 +421,10 @@ namespace chaiscript int line = -1; int col = -1; - private: - std::string::const_iterator m_pos; - std::string::const_iterator m_end; - int m_last_col = -1; + private: + const char *m_pos = nullptr; + const char *m_end = nullptr; + int m_last_col = -1; }; Position m_position; @@ -394,103 +432,86 @@ namespace chaiscript Tracer m_tracer; Optimizer m_optimizer; - void validate_object_name(const std::string &name) const - { + void validate_object_name(std::string_view name) const { if (!Name_Validator::valid_object_name(name)) { - throw exception::eval_error("Invalid Object Name: " + name, File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Invalid Object Name: " + std::string(name), File_Position(m_position.line, m_position.col), *m_filename); } } - public: - explicit ChaiScript_Parser(Tracer tracer = Tracer(), Optimizer optimizer=Optimizer()) - : m_tracer(std::move(tracer)), - m_optimizer(std::move(optimizer)) - { + public: + explicit ChaiScript_Parser(Tracer tracer = Tracer(), Optimizer optimizer = Optimizer()) + : m_tracer(std::move(tracer)) + , m_optimizer(std::move(optimizer)) { m_match_stack.reserve(2); } - Tracer &get_tracer() - { - return m_tracer; - } + Tracer &get_tracer() noexcept { return m_tracer; } - Optimizer &get_optimizer() - { - return m_optimizer; - } + Optimizer &get_optimizer() noexcept { return m_optimizer; } ChaiScript_Parser(const ChaiScript_Parser &) = delete; ChaiScript_Parser &operator=(const ChaiScript_Parser &) = delete; ChaiScript_Parser(ChaiScript_Parser &&) = default; ChaiScript_Parser &operator=(ChaiScript_Parser &&) = delete; + constexpr static auto m_alphabet = build_alphabet(); + constexpr static Operator_Matches m_operator_matches{}; + /// test a char in an m_alphabet - bool char_in_alphabet(char c, detail::Alphabet a) const { return m_alphabet[a][static_cast(c)]; } + 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 { - std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " << t.start().column << '\n'; + std::cout << prepend << "(" << ast_node_type_to_string(t.identifier) << ") " << t.text << " : " << t.start().line << ", " + << t.start().column << '\n'; for (const auto &node : t.get_children()) { debug_print(node.get(), prepend + " "); } } - /// Helper function that collects ast_nodes from a starting position to the top of the stack into a new AST node template void build_match(size_t t_match_start, std::string t_text = "") { bool is_deep = false; - Parse_Location filepos = [&]()->Parse_Location{ - //so we want to take everything to the right of this and make them children + Parse_Location filepos = [&]() -> Parse_Location { + // so we want to take everything to the right of this and make them children if (t_match_start != m_match_stack.size()) { is_deep = true; - return Parse_Location( - m_filename, - m_match_stack[t_match_start]->location.start.line, - m_match_stack[t_match_start]->location.start.column, - m_position.line, - m_position.col - ); + return Parse_Location(m_filename, + m_match_stack[t_match_start]->location.start.line, + m_match_stack[t_match_start]->location.start.column, + m_position.line, + m_position.col); } else { - return Parse_Location( - m_filename, - m_position.line, - m_position.col, - m_position.line, - m_position.col - ); + return Parse_Location(m_filename, m_position.line, m_position.col, m_position.line, m_position.col); } }(); std::vector> new_children; if (is_deep) { - new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast(t_match_start)), + new_children.assign(std::make_move_iterator(m_match_stack.begin() + static_cast(t_match_start)), std::make_move_iterator(m_match_stack.end())); m_match_stack.erase(m_match_stack.begin() + static_cast(t_match_start), m_match_stack.end()); } /// \todo fix the fact that a successful match that captured no ast_nodes doesn't have any real start position m_match_stack.push_back( - m_optimizer.optimize( - chaiscript::make_unique, NodeType>( - std::move(t_text), - std::move(filepos), - std::move(new_children))) - ); + m_optimizer.optimize(chaiscript::make_unique, NodeType>(std::move(t_text), + std::move(filepos), + std::move(new_children)))); } - /// Reads a symbol group from input if it matches the parameter, without skipping initial whitespace - inline auto Symbol_(const utility::Static_String &sym) - { + inline auto Symbol_(const utility::Static_String &sym) noexcept { const auto len = sym.size(); 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]) { return false; } + for (size_t pos = 0; pos < len; ++pos) { + if (sym.c_str()[pos] != file_pos[pos]) { + return false; + } } m_position += len; return true; @@ -500,18 +521,18 @@ namespace chaiscript /// Skips any multi-line or single-line comment bool SkipComment() { - if (Symbol_(multiline_comment_begin())) { + if (Symbol_(m_multiline_comment_begin)) { while (m_position.has_more()) { - if (Symbol_(multiline_comment_end())) { + if (Symbol_(m_multiline_comment_end)) { break; } else if (!Eol_()) { ++m_position; } } return true; - } else if (Symbol_(singleline_comment())) { + } else if (Symbol_(m_singleline_comment)) { while (m_position.has_more()) { - if (Symbol_(cr_lf())) { + if (Symbol_(m_cr_lf)) { m_position -= 2; break; } else if (Char_('\n')) { @@ -522,9 +543,9 @@ namespace chaiscript } } return true; - } else if (Symbol_(annotation())) { + } else if (Symbol_(m_annotation)) { while (m_position.has_more()) { - if (Symbol_(cr_lf())) { + if (Symbol_(m_cr_lf)) { m_position -= 2; break; } else if (Char_('\n')) { @@ -539,23 +560,21 @@ namespace chaiscript return false; } - /// Skips ChaiScript whitespace, which means space and tab, but not cr/lf /// jespada: Modified SkipWS to skip optionally CR ('\n') and/or LF+CR ("\r\n") /// AlekMosingiewicz: Added exception when illegal character detected - bool SkipWS(bool skip_cr=false) { + bool SkipWS(bool skip_cr = false) { bool retval = false; while (m_position.has_more()) { - if(static_cast(*m_position) > 0x7e) { + if (static_cast(*m_position) > 0x7e) { throw exception::eval_error("Illegal character", File_Position(m_position.line, m_position.col), *m_filename); } - auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position+1) == '\n')); + auto end_line = (*m_position != 0) && ((*m_position == '\n') || (*m_position == '\r' && *(m_position + 1) == '\n')); - if ( char_in_alphabet(*m_position,detail::white_alphabet) || (skip_cr && end_line)) { - - if(end_line) { - if(*m_position == '\r') { + if (char_in_alphabet(*m_position, detail::white_alphabet) || (skip_cr && end_line)) { + if (end_line) { + if (*m_position == '\r') { // discards lf ++m_position; } @@ -564,8 +583,7 @@ namespace chaiscript ++m_position; retval = true; - } - else if (SkipComment()) { + } else if (SkipComment()) { retval = true; } else { break; @@ -575,7 +593,7 @@ namespace chaiscript } /// Reads the optional exponent (scientific notation) and suffix for a Float - bool read_exponent_and_suffix() { + bool read_exponent_and_suffix() noexcept { // Support a form of scientific notation: 1e-5, 35.5E+8, 0.01e19 if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { ++m_position; @@ -583,7 +601,7 @@ namespace chaiscript ++m_position; } auto exponent_pos = m_position; - while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_alphabet)) { ++m_position; } if (m_position == exponent_pos) { @@ -593,30 +611,27 @@ namespace chaiscript } // Parse optional float suffix - while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet)) - { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::float_suffix_alphabet)) { ++m_position; } return true; } - /// Reads a floating point value from input, without skipping initial whitespace - bool Float_() { - if (m_position.has_more() && char_in_alphabet(*m_position,detail::float_alphabet) ) { - while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + bool Float_() noexcept { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_alphabet)) { ++m_position; } if (m_position.has_more() && (std::tolower(*m_position) == 'e')) { // The exponent is valid even without any decimal in the Float (1e8, 3e-15) return read_exponent_and_suffix(); - } - else if (m_position.has_more() && (*m_position == '.')) { + } else if (m_position.has_more() && (*m_position == '.')) { ++m_position; - if (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet)) { - while (m_position.has_more() && char_in_alphabet(*m_position,detail::int_alphabet) ) { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::int_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_alphabet)) { ++m_position; } @@ -631,28 +646,25 @@ namespace chaiscript } /// Reads a hex value from input, without skipping initial whitespace - bool Hex_() { + bool Hex_() noexcept { if (m_position.has_more() && (*m_position == '0')) { ++m_position; - if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet) ) { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::x_alphabet)) { ++m_position; if (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) { - while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::hex_alphabet)) { ++m_position; } - while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) - { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) { ++m_position; } return true; - } - else { + } else { --m_position; } - } - else { + } else { --m_position; } } @@ -662,8 +674,7 @@ namespace chaiscript /// Reads an integer suffix void IntSuffix_() { - while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) - { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::int_suffix_alphabet)) { ++m_position; } } @@ -673,10 +684,10 @@ namespace chaiscript if (m_position.has_more() && (*m_position == '0')) { ++m_position; - if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet) ) { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::b_alphabet)) { ++m_position; - if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { - while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet) ) { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet)) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::bin_alphabet)) { ++m_position; } return true; @@ -692,19 +703,16 @@ namespace chaiscript } /// Parses a floating point value and returns a Boxed_Value representation of it - static Boxed_Value buildFloat(const std::string &t_val) - { + static Boxed_Value buildFloat(std::string_view t_val) { bool float_ = false; bool long_ = false; auto i = t_val.size(); - for (; i > 0; --i) - { - char val = t_val[i-1]; + for (; i > 0; --i) { + char val = t_val[i - 1]; - if (val == 'f' || val == 'F') - { + if (val == 'f' || val == 'F') { float_ = true; } else if (val == 'l' || val == 'L') { long_ = true; @@ -713,36 +721,29 @@ namespace chaiscript } } - if (float_) - { - return const_var(parse_num(t_val.substr(0,i))); + if (float_) { + return const_var(parse_num(t_val.substr(0, i))); } else if (long_) { - return const_var(parse_num(t_val.substr(0,i))); + return const_var(parse_num(t_val.substr(0, i))); } else { - return const_var(parse_num(t_val.substr(0,i))); + return const_var(parse_num(t_val.substr(0, i))); } } - - - static Boxed_Value buildInt(const int base, const std::string &t_val, const bool prefixed) - { + static Boxed_Value buildInt(const int base, std::string_view t_val, const bool prefixed) { bool unsigned_ = false; bool long_ = false; bool longlong_ = false; auto i = t_val.size(); - for (; i > 0; --i) - { - const char val = t_val[i-1]; + for (; i > 0; --i) { + const char val = t_val[i - 1]; - if (val == 'u' || val == 'U') - { + if (val == 'u' || val == 'U') { unsigned_ = true; } else if (val == 'l' || val == 'L') { - if (long_) - { + if (long_) { longlong_ = true; } @@ -752,7 +753,9 @@ namespace chaiscript } } - const auto val = prefixed?std::string(t_val.begin()+2,t_val.end()):t_val; + if (prefixed) { + t_val.remove_prefix(2); + } #ifdef __GNUC__ #pragma GCC diagnostic push @@ -760,24 +763,25 @@ namespace chaiscript #ifdef CHAISCRIPT_CLANG #pragma GCC diagnostic ignored "-Wtautological-compare" +#pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" #pragma GCC diagnostic ignored "-Wsign-conversion" #endif #endif try { - auto u = std::stoll(val,nullptr,base); - + /// TODO fix this to use from_chars + auto u = std::stoll(std::string(t_val), nullptr, base); if (!unsigned_ && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); - } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { + } else if ((unsigned_ || base != 10) && !long_ && u >= std::numeric_limits::min() + && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); } else if (!unsigned_ && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); - } else if ((unsigned_ || base != 10) && !longlong_ - - && u >= std::numeric_limits::min() + } else if ((unsigned_ || base != 10) && !longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); } else if (!unsigned_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { @@ -789,7 +793,8 @@ namespace chaiscript } catch (const std::out_of_range &) { // too big to be signed try { - auto u = std::stoull(val,nullptr,base); + /// TODO fix this to use from_chars + auto u = std::stoull(std::string(t_val), nullptr, base); if (!longlong_ && u >= std::numeric_limits::min() && u <= std::numeric_limits::max()) { return const_var(static_cast(u)); @@ -805,13 +810,15 @@ namespace chaiscript #ifdef __GNUC__ #pragma GCC diagnostic pop #endif - } - template - std::unique_ptr> make_node(std::string t_match, const int t_prev_line, const int t_prev_col, Param && ...param) - { - return chaiscript::make_unique, T>(std::move(t_match), Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), std::forward(param)...); + template + std::unique_ptr> + make_node(std::string_view t_match, const int t_prev_line, const int t_prev_col, Param &&...param) { + return chaiscript::make_unique, T>( + std::string(t_match), + Parse_Location(m_filename, t_prev_line, t_prev_col, m_position.line, m_position.col), + std::forward(param)...); } /// Reads a number from the input, detecting if it's an integer or floating point @@ -819,37 +826,35 @@ namespace chaiscript SkipWS(); const auto start = m_position; - if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet) ) { + if (m_position.has_more() && char_in_alphabet(*m_position, detail::float_alphabet)) { try { if (Hex_()) { auto match = Position::str(start, m_position); auto bv = buildInt(16, match, true); - m_match_stack.emplace_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.emplace_back(make_node>(match, start.line, start.col, std::move(bv))); return true; } if (Binary_()) { auto match = Position::str(start, m_position); auto bv = buildInt(2, match, true); - m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(match, start.line, start.col, std::move(bv))); return true; } if (Float_()) { auto match = Position::str(start, m_position); auto bv = buildFloat(match); - m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(match, start.line, start.col, std::move(bv))); return true; - } - else { + } else { IntSuffix_(); auto match = Position::str(start, m_position); if (!match.empty() && (match[0] == '0')) { auto bv = buildInt(8, match, false); - m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); - } - else if (!match.empty()) { + m_match_stack.push_back(make_node>(match, start.line, start.col, std::move(bv))); + } else if (!match.empty()) { auto bv = buildInt(10, match, false); - m_match_stack.push_back(make_node>(std::move(match), start.line, start.col, std::move(bv))); + m_match_stack.push_back(make_node>(match, start.line, start.col, std::move(bv))); } else { return false; } @@ -859,8 +864,7 @@ namespace chaiscript // error parsing number passed in to buildFloat/buildInt return false; } - } - else { + } else { return false; } } @@ -868,7 +872,7 @@ namespace chaiscript /// Reads an identifier from input which conforms to C's identifier naming conventions, without skipping initial whitespace bool Id_() { if (m_position.has_more() && char_in_alphabet(*m_position, detail::id_alphabet)) { - while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + while (m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet)) { ++m_position; } @@ -879,17 +883,17 @@ namespace chaiscript while (m_position.has_more() && (*m_position != '`')) { if (Eol()) { - throw exception::eval_error("Carriage return in identifier literal", File_Position(m_position.line, m_position.col), *m_filename); - } - else { + throw exception::eval_error("Carriage return in identifier literal", + File_Position(m_position.line, m_position.col), + *m_filename); + } else { ++m_position; } } if (start == m_position) { throw exception::eval_error("Missing contents of identifier literal", File_Position(m_position.line, m_position.col), *m_filename); - } - else if (!m_position.has_more()) { + } else if (!m_position.has_more()) { throw exception::eval_error("Incomplete identifier literal", File_Position(m_position.line, m_position.col), *m_filename); } @@ -906,9 +910,8 @@ namespace chaiscript const auto start = m_position; if (Id_()) { - auto text = Position::str(start, m_position); - const auto text_hash = utility::fnv1a_32(text.c_str()); + const auto text_hash = utility::hash(text); if (validate) { validate_object_name(text); @@ -920,64 +923,66 @@ namespace chaiscript #endif switch (text_hash) { - case utility::fnv1a_32("true"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, const_var(true))); + case utility::hash("true"): { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(true))); } break; - case utility::fnv1a_32("false"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, const_var(false))); + case utility::hash("false"): { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(false))); } break; - case utility::fnv1a_32("Infinity"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(std::numeric_limits::infinity()))); + case utility::hash("Infinity"): { + m_match_stack.push_back(make_node>(text, + start.line, + start.col, + const_var(std::numeric_limits::infinity()))); } break; - case utility::fnv1a_32("NaN"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(std::numeric_limits::quiet_NaN()))); + case utility::hash("NaN"): { + m_match_stack.push_back(make_node>(text, + start.line, + start.col, + const_var(std::numeric_limits::quiet_NaN()))); } break; - case utility::fnv1a_32("__LINE__"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(start.line))); + case utility::hash("__LINE__"): { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(start.line))); } break; - case utility::fnv1a_32("__FILE__"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(m_filename))); + case utility::hash("__FILE__"): { + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(m_filename))); } break; - case utility::fnv1a_32("__FUNC__"): { + case utility::hash("__FUNC__"): { std::string fun_name = "NOT_IN_FUNCTION"; - for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 0; --idx) - { - if (m_match_stack[idx-1]->identifier == AST_Node_Type::Id - && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { - fun_name = m_match_stack[idx-1]->text; + for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 0; --idx) { + if (m_match_stack[idx - 1]->identifier == AST_Node_Type::Id + && m_match_stack[idx - 0]->identifier == AST_Node_Type::Arg_List) { + fun_name = m_match_stack[idx - 1]->text; } } - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(fun_name))); + m_match_stack.push_back(make_node>(text, start.line, start.col, const_var(fun_name))); } break; - case utility::fnv1a_32("__CLASS__"): { + case utility::hash("__CLASS__"): { std::string fun_name = "NOT_IN_CLASS"; - for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 1; --idx) - { - if (m_match_stack[idx-2]->identifier == AST_Node_Type::Id - && m_match_stack[idx-1]->identifier == AST_Node_Type::Id - && m_match_stack[idx-0]->identifier == AST_Node_Type::Arg_List) { - fun_name = m_match_stack[idx-2]->text; + for (size_t idx = m_match_stack.empty() ? 0 : m_match_stack.size() - 1; idx > 1; --idx) { + if (m_match_stack[idx - 2]->identifier == AST_Node_Type::Id && m_match_stack[idx - 1]->identifier == AST_Node_Type::Id + && m_match_stack[idx - 0]->identifier == AST_Node_Type::Arg_List) { + fun_name = m_match_stack[idx - 2]->text; } } - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - const_var(fun_name))); + m_match_stack.push_back( + make_node>(std::move(text), start.line, start.col, const_var(fun_name))); } break; - case utility::fnv1a_32("_"): { - m_match_stack.push_back(make_node>(std::move(text), start.line, start.col, - Boxed_Value(std::make_shared()))); + case utility::hash("_"): { + m_match_stack.push_back( + make_node>(std::move(text), + start.line, + start.col, + Boxed_Value(std::make_shared()))); } break; default: { - std::string val = std::move(text); + auto val = text; if (*start == '`') { // 'escaped' literal, like an operator name - val = Position::str(start+1, m_position-1); + val = Position::str(start + 1, m_position - 1); + // val.remove_prefix(1); val.remove_suffix(1); } m_match_stack.push_back(make_node>(val, start.line, start.col)); } break; @@ -987,7 +992,6 @@ namespace chaiscript #pragma warning(pop) #endif - return true; } else { return false; @@ -1014,8 +1018,6 @@ namespace chaiscript return true; } - - /// Reads a quoted string from input, without skipping initial whitespace bool Quoted_String_() { if (m_position.has_more() && (*m_position == '\"')) { @@ -1026,7 +1028,6 @@ namespace chaiscript bool in_quote = false; while (m_position.has_more() && ((*m_position != '\"') || (in_interpolation > 0) || (prev_char == '\\'))) { - if (!Eol_()) { if (prev_char == '$' && *m_position == '{') { ++in_interpolation; @@ -1057,10 +1058,9 @@ namespace chaiscript } template - struct Char_Parser - { + struct Char_Parser { string_type &match; - typedef typename string_type::value_type char_type; + using char_type = typename string_type::value_type; bool is_escaped = false; bool is_interpolated = false; bool saw_interpolation_marker = false; @@ -1073,14 +1073,13 @@ namespace chaiscript string_type hex_matches; Char_Parser(string_type &t_match, const bool t_interpolation_allowed) - : match(t_match), - interpolation_allowed(t_interpolation_allowed) - { + : match(t_match) + , interpolation_allowed(t_interpolation_allowed) { } Char_Parser &operator=(const Char_Parser &) = delete; - ~Char_Parser(){ + ~Char_Parser() { try { if (is_octal) { process_octal(); @@ -1099,8 +1098,7 @@ namespace chaiscript } } - void process_hex() - { + void process_hex() { if (!hex_matches.empty()) { auto val = stoll(hex_matches, nullptr, 16); match.push_back(char_type(val)); @@ -1110,9 +1108,7 @@ namespace chaiscript is_hex = false; } - - void process_octal() - { + void process_octal() { if (!octal_matches.empty()) { auto val = stoll(octal_matches, nullptr, 8); match.push_back(char_type(val)); @@ -1122,9 +1118,7 @@ namespace chaiscript is_octal = false; } - - void process_unicode() - { + void process_unicode() { const auto ch = static_cast(std::stoi(hex_matches, nullptr, 16)); const auto match_size = hex_matches.size(); hex_matches.clear(); @@ -1140,7 +1134,6 @@ namespace chaiscript throw exception::eval_error("Invalid 16 bit universal character"); } - if (ch < 0x80) { match += static_cast(ch); } else if (ch < 0x800) { @@ -1148,15 +1141,15 @@ namespace chaiscript 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)); + 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[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)); + 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? @@ -1167,9 +1160,7 @@ namespace chaiscript void parse(const char_type t_char, const int line, const int col, const std::string &filename) { const bool is_octal_char = t_char >= '0' && t_char <= '7'; - const bool is_hex_char = (t_char >= '0' && t_char <= '9') - || (t_char >= 'a' && t_char <= 'f') - || (t_char >= 'A' && t_char <= 'F'); + const bool is_hex_char = (t_char >= '0' && t_char <= '9') || (t_char >= 'a' && t_char <= 'f') || (t_char >= 'A' && t_char <= 'F'); if (is_octal) { if (is_octal_char) { @@ -1186,7 +1177,7 @@ namespace chaiscript if (is_hex_char) { hex_matches.push_back(t_char); - if (hex_matches.size() == 2*sizeof(char_type)) { + if (hex_matches.size() == 2 * sizeof(char_type)) { // This rule differs from the C/C++ standard, but ChaiScript // does not offer the same workaround options, and having // hexadecimal sequences longer than can fit into the char @@ -1201,7 +1192,7 @@ namespace chaiscript if (is_hex_char) { hex_matches.push_back(t_char); - if(hex_matches.size() == unicode_size) { + if (hex_matches.size() == unicode_size) { // Format is specified to be 'slash'uABCD // on collecting from A to D do parsing process_unicode(); @@ -1234,18 +1225,41 @@ namespace chaiscript unicode_size = 8; } else { switch (t_char) { - case ('\'') : match.push_back('\''); break; - case ('\"') : match.push_back('\"'); break; - case ('?') : match.push_back('?'); break; - case ('a') : match.push_back('\a'); break; - case ('b') : match.push_back('\b'); break; - case ('f') : match.push_back('\f'); break; - case ('n') : match.push_back('\n'); break; - case ('r') : match.push_back('\r'); break; - case ('t') : match.push_back('\t'); break; - case ('v') : match.push_back('\v'); break; - case ('$') : match.push_back('$'); break; - default: throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); + case ('\''): + match.push_back('\''); + break; + case ('\"'): + match.push_back('\"'); + break; + case ('?'): + match.push_back('?'); + break; + case ('a'): + match.push_back('\a'); + break; + case ('b'): + match.push_back('\b'); + break; + case ('f'): + match.push_back('\f'); + break; + case ('n'): + match.push_back('\n'); + break; + case ('r'): + match.push_back('\r'); + break; + case ('t'): + match.push_back('\t'); + break; + case ('v'): + match.push_back('\v'); + break; + case ('$'): + match.push_back('$'); + break; + default: + throw exception::eval_error("Unknown escaped sequence in string", File_Position(line, col), filename); } is_escaped = false; } @@ -1256,10 +1270,8 @@ namespace chaiscript } } } - }; - /// Reads (and potentially captures) a quoted string from input. Translates escaped sequences. bool Quoted_String() { Depth_Counter dc{this}; @@ -1271,25 +1283,24 @@ namespace chaiscript std::string match; const auto prev_stack_top = m_match_stack.size(); - bool is_interpolated = [&]()->bool { + bool is_interpolated = [&]() -> bool { Char_Parser cparser(match, true); - auto s = start + 1, end = m_position - 1; while (s != end) { if (cparser.saw_interpolation_marker) { if (*s == '{') { - //We've found an interpolation point + // We've found an interpolation point m_match_stack.push_back(make_node>(match, start.line, start.col, const_var(match))); if (cparser.is_interpolated) { - //If we've seen previous interpolation, add on instead of making a new one + // If we've seen previous interpolation, add on instead of making a new one build_match>(prev_stack_top, "+"); } - //We've finished with the part of the string up to this point, so clear it + // We've finished with the part of the string up to this point, so clear it match.clear(); std::string eval_match; @@ -1333,6 +1344,10 @@ namespace chaiscript } } + if (cparser.saw_interpolation_marker) { + match.push_back('$'); + } + return cparser.is_interpolated; }(); @@ -1395,13 +1410,14 @@ namespace chaiscript } if (match.size() != 1) { - throw exception::eval_error("Single-quoted strings must be 1 character long", File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Single-quoted strings must be 1 character long", + File_Position(m_position.line, m_position.col), + *m_filename); } m_match_stack.push_back(make_node>(match, start.line, start.col, const_var(char(match.at(0))))); return true; - } - else { + } else { return false; } } @@ -1448,7 +1464,7 @@ namespace chaiscript const auto start = m_position; bool retval = Keyword_(t_s); // ignore substring matches - if ( retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet) ) { + if (retval && m_position.has_more() && char_in_alphabet(*m_position, detail::keyword_alphabet)) { m_position = start; retval = false; } @@ -1456,26 +1472,18 @@ namespace chaiscript return retval; } - bool is_operator(const std::string &t_s) const { - return std::any_of(m_operator_matches.begin(), m_operator_matches.end(), - [t_s](const std::vector &opers) { - return std::any_of(opers.begin(), opers.end(), - [t_s](const utility::Static_String &s) { - return t_s == s.c_str(); - }); - }); - } + 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) { + bool Symbol(const utility::Static_String &t_s, const bool t_disallow_prevention = false) { Depth_Counter dc{this}; SkipWS(); const auto start = m_position; bool retval = Symbol_(t_s); // ignore substring matches - if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position,detail::symbol_alphabet)) { - if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position+1))) { + if (retval && m_position.has_more() && (t_disallow_prevention == false) && char_in_alphabet(*m_position, detail::symbol_alphabet)) { + if (*m_position != '=' && is_operator(Position::str(start, m_position)) && !is_operator(Position::str(start, m_position + 1))) { // don't throw this away, it's a good match and the next is not } else { m_position = start; @@ -1490,7 +1498,7 @@ namespace chaiscript bool Eol_(const bool t_eos = false) { bool retval = false; - if (m_position.has_more() && (Symbol_(cr_lf()) || Char_('\n'))) { + if (m_position.has_more() && (Symbol_(m_cr_lf) || Char_('\n'))) { retval = true; //++m_position.line; m_position.col = 1; @@ -1527,14 +1535,16 @@ namespace chaiscript if (Arg(false)) { retval = true; - while (Eol()) {} + while (Eol()) { + } while (Char(',')) { - while (Eol()) {} + while (Eol()) { + } if (!Arg(false)) { throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } - } + } } build_match>(prev_stack_top); @@ -1553,10 +1563,12 @@ namespace chaiscript if (Arg()) { retval = true; - while (Eol()) {} + while (Eol()) { + } while (Char(',')) { - while (Eol()) {} + while (Eol()) { + } if (!Arg()) { throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1569,7 +1581,6 @@ namespace chaiscript return retval; } - /// Reads a comma-separated list of values from input bool Arg_List() { Depth_Counter dc{this}; @@ -1580,9 +1591,11 @@ namespace chaiscript if (Equation()) { retval = true; - while (Eol()) {} + while (Eol()) { + } while (Char(',')) { - while (Eol()) {} + while (Eol()) { + } if (!Equation()) { throw exception::eval_error("Unexpected value in parameter list", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1609,9 +1622,11 @@ namespace chaiscript build_match>(prev_stack_top); } else if (Map_Pair()) { retval = true; - while (Eol()) {} + while (Eol()) { + } while (Char(',')) { - while (Eol()) {} + while (Eol()) { + } if (!Map_Pair()) { throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1619,9 +1634,11 @@ namespace chaiscript build_match>(prev_stack_top); } else if (Operator()) { retval = true; - while (Eol()) {} + while (Eol()) { + } while (Char(',')) { - while (Eol()) {} + while (Eol()) { + } if (!Operator()) { throw exception::eval_error("Unexpected value in container", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1663,8 +1680,8 @@ namespace chaiscript throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); } - - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete anonymous function", File_Position(m_position.line, m_position.col), *m_filename); @@ -1697,7 +1714,7 @@ namespace chaiscript bool is_method = false; if (Symbol("::")) { - //We're now a method + // We're now a method is_method = true; if (!Id(true)) { @@ -1712,15 +1729,19 @@ namespace chaiscript } } - while (Eos()) {} + while (Eos()) { + } if (Char(':')) { if (!Operator()) { - throw exception::eval_error("Missing guard expression for function", File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Missing guard expression for function", + File_Position(m_position.line, m_position.col), + *m_filename); } } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete function definition", File_Position(m_position.line, m_position.col), *m_filename); } @@ -1730,7 +1751,6 @@ namespace chaiscript } else { build_match>(prev_stack_top); } - } return retval; @@ -1746,7 +1766,8 @@ namespace chaiscript if (Keyword("try")) { retval = true; - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'try' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1754,22 +1775,21 @@ namespace chaiscript bool has_matches = true; while (has_matches) { - while (Eol()) {} + while (Eol()) { + } has_matches = false; if (Keyword("catch")) { const auto catch_stack_top = m_match_stack.size(); if (Char('(')) { if (!(Arg() && Char(')'))) { - throw exception::eval_error("Incomplete 'catch' expression", File_Position(m_position.line, m_position.col), *m_filename); - } - if (Char(':')) { - if (!Operator()) { - throw exception::eval_error("Missing guard expression for catch", File_Position(m_position.line, m_position.col), *m_filename); - } + throw exception::eval_error("Incomplete 'catch' expression", + File_Position(m_position.line, m_position.col), + *m_filename); } } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'catch' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1778,11 +1798,13 @@ namespace chaiscript has_matches = true; } } - while (Eol()) {} + while (Eol()) { + } if (Keyword("finally")) { const auto finally_stack_top = m_match_stack.size(); - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'finally' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1820,7 +1842,8 @@ namespace chaiscript throw exception::eval_error("Incomplete 'if' expression", File_Position(m_position.line, m_position.col), *m_filename); } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'if' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1828,13 +1851,15 @@ namespace chaiscript bool has_matches = true; while (has_matches) { - while (Eol()) {} + while (Eol()) { + } has_matches = false; if (Keyword("else")) { if (If()) { has_matches = true; } else { - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'else' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1846,15 +1871,14 @@ namespace chaiscript const auto num_children = m_match_stack.size() - prev_stack_top; - if ((is_if_init && num_children == 3) - || (!is_if_init && num_children == 2)) { + if ((is_if_init && num_children == 3) || (!is_if_init && num_children == 2)) { m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } if (!is_if_init) { build_match>(prev_stack_top); } else { - build_match>(prev_stack_top+1); + build_match>(prev_stack_top + 1); build_match>(prev_stack_top); } } @@ -1871,7 +1895,9 @@ namespace chaiscript if (Keyword("class")) { if (!t_class_allowed) { - throw exception::eval_error("Class definitions only allowed at top scope", File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Class definitions only allowed at top scope", + File_Position(m_position.line, m_position.col), + *m_filename); } retval = true; @@ -1882,7 +1908,8 @@ namespace chaiscript const auto class_name = m_match_stack.back()->text; - while (Eol()) {} + while (Eol()) { + } if (!Class_Block(class_name)) { throw exception::eval_error("Incomplete 'class' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1894,7 +1921,6 @@ namespace chaiscript return retval; } - /// Reads a while block from input bool While() { Depth_Counter dc{this}; @@ -1913,7 +1939,8 @@ namespace chaiscript throw exception::eval_error("Incomplete 'while' expression", File_Position(m_position.line, m_position.col), *m_filename); } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'while' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -1932,39 +1959,33 @@ namespace chaiscript return Char(':') && Equation(); } - /// Reads the C-style `for` conditions from input bool For_Guards() { Depth_Counter dc{this}; - if (!(Equation() && Eol())) - { - if (!Eol()) - { + if (!(Equation() && Eol())) { + if (!Eol()) { return false; } else { m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } } - if (!(Equation() && Eol())) - { - if (!Eol()) - { + if (!(Equation() && Eol())) { + if (!Eol()) { return false; } else { - m_match_stack.push_back(chaiscript::make_unique, eval::Constant_AST_Node>(Boxed_Value(true))); + m_match_stack.push_back( + chaiscript::make_unique, eval::Constant_AST_Node>(Boxed_Value(true))); } } - if (!Equation()) - { + if (!Equation()) { m_match_stack.push_back(chaiscript::make_unique, eval::Noop_AST_Node>()); } - return true; + return true; } - /// Reads a for block from input bool For() { Depth_Counter dc{this}; @@ -1984,7 +2005,8 @@ namespace chaiscript throw exception::eval_error("Incomplete 'for' expression", File_Position(m_position.line, m_position.col), *m_filename); } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'for' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -2008,7 +2030,6 @@ namespace chaiscript return retval; } - /// Reads a case block from input bool Case() { Depth_Counter dc{this}; @@ -2027,7 +2048,8 @@ namespace chaiscript throw exception::eval_error("Incomplete 'case' expression", File_Position(m_position.line, m_position.col), *m_filename); } - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'case' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -2037,7 +2059,8 @@ namespace chaiscript } else if (Keyword("default")) { retval = true; - while (Eol()) {} + while (Eol()) { + } if (!Block()) { throw exception::eval_error("Incomplete 'default' block", File_Position(m_position.line, m_position.col), *m_filename); @@ -2049,14 +2072,12 @@ namespace chaiscript return retval; } - /// Reads a switch statement from input bool Switch() { Depth_Counter dc{this}; const auto prev_stack_top = m_match_stack.size(); if (Keyword("switch")) { - if (!Char('(')) { throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); } @@ -2065,22 +2086,25 @@ namespace chaiscript throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_position.line, m_position.col), *m_filename); } - while (Eol()) {} + while (Eol()) { + } if (Char('{')) { - while (Eol()) {} - - while (Case()) { - while (Eol()) { } // eat + while (Eol()) { } - while (Eol()) { } // eat + while (Case()) { + while (Eol()) { + } // eat + } + + while (Eol()) { + } // eat if (!Char('}')) { throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } - } - else { + } else { throw exception::eval_error("Incomplete block", File_Position(m_position.line, m_position.col), *m_filename); } @@ -2090,10 +2114,8 @@ namespace chaiscript } else { return false; } - } - /// Reads a curly-brace C-style class block from input bool Class_Block(const std::string &t_class_name) { Depth_Counter dc{this}; @@ -2190,9 +2212,7 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - if (Lambda() || Num() || Quoted_String() || Single_Quoted_String() || - Paren_Expression() || Inline_Container() || Id(false)) - { + if (Lambda() || Num() || Quoted_String() || Single_Quoted_String() || Paren_Expression() || Inline_Container() || Id(false)) { retval = true; bool has_more = true; @@ -2211,21 +2231,33 @@ namespace chaiscript /// \todo Work around for method calls until we have a better solution if (!m_match_stack.back()->children.empty()) { if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Dot_Access) { - if (m_match_stack.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); -} - if (m_match_stack.back()->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); -} + if (m_match_stack.empty()) { + throw exception::eval_error("Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } + if (m_match_stack.back()->children.empty()) { + throw exception::eval_error("Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } auto dot_access = std::move(m_match_stack.back()->children[0]); auto func_call = std::move(m_match_stack.back()); m_match_stack.pop_back(); func_call->children.erase(func_call->children.begin()); - if (dot_access->children.empty()) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); -} + if (dot_access->children.empty()) { + throw exception::eval_error("Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } func_call->children.insert(func_call->children.begin(), std::move(dot_access->children.back())); dot_access->children.pop_back(); dot_access->children.push_back(std::move(func_call)); - if (dot_access->children.size() != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); -} + if (dot_access->children.size() != 2) { + throw exception::eval_error("Incomplete dot access fun call", + File_Position(m_position.line, m_position.col), + *m_filename); + } m_match_stack.push_back(std::move(dot_access)); } } @@ -2237,17 +2269,26 @@ namespace chaiscript } build_match>(prev_stack_top); - } - else if (Symbol(".")) { + } else if (Symbol(".")) { has_more = true; if (!(Id(true))) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } - if ( std::distance(m_match_stack.begin() + static_cast(prev_stack_top), m_match_stack.end()) != 2) { + if (std::distance(m_match_stack.begin() + static_cast(prev_stack_top), m_match_stack.end()) != 2) { throw exception::eval_error("Incomplete dot access fun call", File_Position(m_position.line, m_position.col), *m_filename); } build_match>(prev_stack_top); + } else if (Eol()) { + auto start = --m_position; + while (Eol()) { + } + if (Symbol(".")) { + has_more = true; + --m_position; + } else { + m_position = start; + } } } } @@ -2272,7 +2313,7 @@ namespace chaiscript } build_match>(prev_stack_top); - } else if (Keyword("auto") || Keyword("var") ) { + } else if (Keyword("auto") || Keyword("var")) { retval = true; if (Reference()) { @@ -2304,7 +2345,6 @@ namespace chaiscript throw exception::eval_error("Missing attribute name in definition", File_Position(m_position.line, m_position.col), *m_filename); } - build_match>(prev_stack_top); } @@ -2336,20 +2376,19 @@ namespace chaiscript Container_Arg_List(); if (!Char(']')) { - throw exception::eval_error("Missing closing square bracket ']' in container initializer", File_Position(m_position.line, m_position.col), *m_filename); + throw exception::eval_error("Missing closing square bracket ']' in container initializer", + File_Position(m_position.line, m_position.col), + *m_filename); } if ((prev_stack_top != m_match_stack.size()) && (!m_match_stack.back()->children.empty())) { if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Value_Range) { build_match>(prev_stack_top); - } - else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) { + } else if (m_match_stack.back()->children[0]->identifier == AST_Node_Type::Map_Pair) { build_match>(prev_stack_top); - } - else { + } else { build_match>(prev_stack_top); } - } - else { + } else { build_match>(prev_stack_top); } @@ -2381,22 +2420,15 @@ namespace chaiscript Depth_Counter dc{this}; const auto prev_stack_top = m_match_stack.size(); using SS = utility::Static_String; - constexpr const std::array prefix_opers{{ - SS{"++"}, - SS{"--"}, - SS{"-"}, - SS{"+"}, - SS{"!"}, - SS{"~"} - }}; + const std::array prefix_opers{{SS{"++"}, SS{"--"}, SS{"-"}, SS{"+"}, SS{"!"}, SS{"~"}}}; - for (const auto &oper : prefix_opers) - { + for (const auto &oper : prefix_opers) { const bool is_char = oper.size() == 1; - if ((is_char && Char(oper.c_str()[0])) || (!is_char && Symbol(oper))) - { - if (!Operator(m_operators.size()-1)) { - throw exception::eval_error("Incomplete prefix '" + std::string(oper.c_str()) + "' expression", File_Position(m_position.line, m_position.col), *m_filename); + if ((is_char && Char(oper.c_str()[0])) || (!is_char && Symbol(oper))) { + if (!Operator(m_operators.size() - 1)) { + throw exception::eval_error("Incomplete prefix '" + std::string(oper.c_str()) + "' expression", + File_Position(m_position.line, m_position.col), + *m_filename); } build_match>(prev_stack_top, oper.c_str()); @@ -2414,14 +2446,14 @@ namespace chaiscript } bool Operator_Helper(const size_t t_precedence, std::string &oper) { - Depth_Counter dc{this}; - for (auto & elem : m_operator_matches[t_precedence]) { + return m_operator_matches.any_of(t_precedence, [&oper, this](const auto &elem) { if (Symbol(elem)) { oper = elem.c_str(); return true; + } else { + return false; } - } - return false; + }); } bool Operator(const size_t t_precedence = 0) { @@ -2429,55 +2461,59 @@ namespace chaiscript bool retval = false; const auto prev_stack_top = m_match_stack.size(); - if (m_operators[t_precedence] != Operator_Precidence::Prefix) { - if (Operator(t_precedence+1)) { + if (m_operators[t_precedence] != Operator_Precedence::Prefix) { + if (Operator(t_precedence + 1)) { retval = true; std::string oper; while (Operator_Helper(t_precedence, oper)) { - while (Eol()) {} - if (!Operator(t_precedence+1)) { + while (Eol()) { + } + if (!Operator(t_precedence + 1)) { throw exception::eval_error("Incomplete '" + oper + "' expression", - File_Position(m_position.line, m_position.col), *m_filename); + File_Position(m_position.line, m_position.col), + *m_filename); } switch (m_operators[t_precedence]) { - case(Operator_Precidence::Ternary_Cond) : + case (Operator_Precedence::Ternary_Cond): if (Symbol(":")) { - if (!Operator(t_precedence+1)) { + if (!Operator(t_precedence + 1)) { throw exception::eval_error("Incomplete '" + oper + "' expression", - File_Position(m_position.line, m_position.col), *m_filename); + File_Position(m_position.line, m_position.col), + *m_filename); } build_match>(prev_stack_top); - } - else { + } else { throw exception::eval_error("Incomplete '" + oper + "' expression", - File_Position(m_position.line, m_position.col), *m_filename); + File_Position(m_position.line, m_position.col), + *m_filename); } break; - case(Operator_Precidence::Addition) : - case(Operator_Precidence::Multiplication) : - case(Operator_Precidence::Shift) : - case(Operator_Precidence::Equality) : - case(Operator_Precidence::Bitwise_And) : - case(Operator_Precidence::Bitwise_Xor) : - case(Operator_Precidence::Bitwise_Or) : - case(Operator_Precidence::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_Precidence::Logical_And) : + case (Operator_Precedence::Logical_And): build_match>(prev_stack_top, oper); break; - case(Operator_Precidence::Logical_Or) : + case (Operator_Precedence::Logical_Or): build_match>(prev_stack_top, oper); break; - case(Operator_Precidence::Prefix) : + case (Operator_Precedence::Prefix): assert(false); // cannot reach here because of if() statement at the top break; -// default: -// throw exception::eval_error("Internal error: unhandled ast_node", File_Position(m_position.line, m_position.col), *m_filename); + // default: + // throw exception::eval_error("Internal error: unhandled ast_node", + // File_Position(m_position.line, m_position.col), *m_filename); } } } @@ -2504,8 +2540,7 @@ namespace chaiscript } build_match>(prev_stack_top); - } - else { + } else { m_position = prev_pos; while (prev_stack_top != m_match_stack.size()) { m_match_stack.pop_back(); @@ -2532,8 +2567,7 @@ namespace chaiscript } build_match>(prev_stack_top); - } - else { + } else { m_position = prev_pos; while (prev_stack_top != m_match_stack.size()) { m_match_stack.pop_back(); @@ -2552,8 +2586,8 @@ namespace chaiscript using SS = utility::Static_String; if (Operator()) { - for (const auto &sym : {SS{"="}, SS{":="}, SS{"+="}, SS{"-="}, SS{"*="}, SS{"/="}, SS{"%="}, SS{"<<="}, SS{">>="}, SS{"&="}, SS{"^="}, SS{"|="}}) - { + for (const auto &sym : + {SS{"="}, SS{":="}, SS{"+="}, SS{"-="}, SS{"*="}, SS{"/="}, SS{"%="}, SS{"<<="}, SS{">>="}, SS{"&="}, SS{"^="}, SS{"|="}}) { if (Symbol(sym, true)) { SkipWS(true); if (!Equation()) { @@ -2582,7 +2616,9 @@ namespace chaiscript const auto start = m_position; if (Def(true, t_class_name) || Var_Decl(true, t_class_name)) { if (!saw_eol) { - throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); + throw exception::eval_error("Two function definitions missing line separator", + File_Position(start.line, start.col), + *m_filename); } has_more = true; retval = true; @@ -2611,26 +2647,25 @@ namespace chaiscript const auto start = m_position; if (Def() || Try() || If() || While() || Class(t_class_allowed) || For() || Switch()) { if (!saw_eol) { - throw exception::eval_error("Two function definitions missing line separator", File_Position(start.line, start.col), *m_filename); + throw exception::eval_error("Two function definitions missing line separator", + File_Position(start.line, start.col), + *m_filename); } has_more = true; retval = true; saw_eol = true; - } - else if (Return() || Break() || Continue() || Equation()) { + } else if (Return() || Break() || Continue() || Equation()) { if (!saw_eol) { throw exception::eval_error("Two expressions missing line separator", File_Position(start.line, start.col), *m_filename); } has_more = true; retval = true; saw_eol = false; - } - else if (Block() || Eol()) { + } else if (Block() || Eol()) { has_more = true; retval = true; saw_eol = true; - } - else { + } else { has_more = false; } } @@ -2638,16 +2673,14 @@ namespace chaiscript return retval; } - AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override - { + AST_NodePtr parse(const std::string &t_input, const std::string &t_fname) override { ChaiScript_Parser parser(m_tracer, m_optimizer); return parser.parse_internal(t_input, t_fname); } - eval::AST_Node_Impl_Ptr parse_instr_eval(const std::string &t_input) - { - auto last_position = m_position; - auto last_filename = m_filename; + eval::AST_Node_Impl_Ptr parse_instr_eval(const std::string &t_input) { + auto last_position = m_position; + auto last_filename = m_filename; auto last_match_stack = std::exchange(m_match_stack, decltype(m_match_stack){}); auto retval = parse_internal(t_input, "instr eval"); @@ -2656,12 +2689,14 @@ namespace chaiscript m_filename = std::move(last_filename); m_match_stack = std::move(last_match_stack); - return eval::AST_Node_Impl_Ptr(dynamic_cast*>(retval.release())); + return eval::AST_Node_Impl_Ptr(dynamic_cast *>(retval.release())); } /// 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) { - m_position = Position(t_input.begin(), t_input.end()); + const auto begin = t_input.empty() ? nullptr : &t_input.front(); + const auto end = begin == nullptr ? nullptr : begin + t_input.size(); + m_position = Position(begin, end); m_filename = std::make_shared(std::move(t_fname)); if ((t_input.size() > 1) && (t_input[0] == '#') && (t_input[1] == '!')) { @@ -2685,8 +2720,8 @@ namespace chaiscript return retval; } }; - } -} + } // namespace parser +} // namespace chaiscript #if defined(CHAISCRIPT_MSVC) && defined(CHAISCRIPT_PUSHED_MIN_MAX) #undef CHAISCRIPT_PUSHED_MIN_MAX @@ -2694,6 +2729,4 @@ namespace chaiscript #pragma pop_macro("max") #endif - #endif /* CHAISCRIPT_PARSER_HPP_ */ - diff --git a/include/chaiscript/language/chaiscript_posix.hpp b/include/chaiscript/language/chaiscript_posix.hpp index 87b66881..48d5eb53 100644 --- a/include/chaiscript/language/chaiscript_posix.hpp +++ b/include/chaiscript/language/chaiscript_posix.hpp @@ -1,68 +1,53 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_POSIX_HPP_ #define CHAISCRIPT_POSIX_HPP_ -namespace chaiscript -{ - namespace detail - { - struct Loadable_Module - { - struct DLModule - { - explicit DLModule(const std::string &t_filename) - : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) - { - if (m_data == nullptr) - { - throw chaiscript::exception::load_module_error(dlerror()); - } +namespace chaiscript::detail { + struct Loadable_Module { + struct DLModule { + explicit DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) { + if (m_data == nullptr) { + throw chaiscript::exception::load_module_error(dlerror()); } - - DLModule(DLModule &&) = default; - DLModule &operator=(DLModule &&) = default; - DLModule(const DLModule &) = delete; - DLModule &operator=(const DLModule &) = delete; - - ~DLModule() - { - dlclose(m_data); - } - - void *m_data; - }; - - template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw chaiscript::exception::load_module_error(dlerror()); - } - } - - T m_symbol; - }; - - Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { } - DLModule m_dlmodule; - DLSym m_func; - ModulePtr m_moduleptr; - }; - } -} -#endif + DLModule(DLModule &&) = default; + DLModule &operator=(DLModule &&) = default; + DLModule(const DLModule &) = delete; + DLModule &operator=(const DLModule &) = delete; + ~DLModule() { dlclose(m_data); } + + void *m_data; + }; + + template + struct DLSym { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) { + if (!m_symbol) { + throw chaiscript::exception::load_module_error(dlerror()); + } + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename) + , m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name) + , m_moduleptr(m_func.m_symbol()) { + } + + DLModule m_dlmodule; + DLSym m_func; + ModulePtr m_moduleptr; + }; +} // namespace chaiscript::detail +#endif diff --git a/include/chaiscript/language/chaiscript_prelude.hpp b/include/chaiscript/language/chaiscript_prelude.hpp index a4deb874..b10bec66 100644 --- a/include/chaiscript/language/chaiscript_prelude.hpp +++ b/include/chaiscript/language/chaiscript_prelude.hpp @@ -1,43 +1,44 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// and 2009-2017, Jason Turner (jason@emptycrate.com) +// and 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_PRELUDE_HPP_ #define CHAISCRIPT_PRELUDE_HPP_ namespace chaiscript { -struct ChaiScript_Prelude { - static std::string chaiscript_prelude() { return R"chaiscript( + struct ChaiScript_Prelude { + static std::string chaiscript_prelude() { + return R"chaiscript( -def lt(l, r) { - if (call_exists(`<`, l, r)) { - l < r - } else { - type_name(l) < type_name(r) - } +def lt(l, r) { + if (call_exists(`<`, l, r)) { + l < r + } else { + type_name(l) < type_name(r) + } } -def gt(l, r) { - if (call_exists(`>`, l, r)) { - l > r - } else { - type_name(l) > type_name(r) - } +def gt(l, r) { + if (call_exists(`>`, l, r)) { + l > r + } else { + type_name(l) > type_name(r) + } } -def eq(l, r) { - if (call_exists(`==`, l, r)) { - l == r - } else { - false - } +def eq(l, r) { + if (call_exists(`==`, l, r)) { + l == r + } else { + false + } } -def new(x) { - eval(type_name(x))(); +def new(x) { + eval(type_name(x))(); } def clone(double x) { @@ -64,85 +65,85 @@ def clone(x) : function_exists(type_name(x)) && call_exists(eval(type_name(x)), # to_string for Pair() -def to_string(x) : call_exists(first, x) && call_exists(second, x) { - "<" + x.first.to_string() + ", " + x.second.to_string() + ">"; +def to_string(x) : call_exists(first, x) && call_exists(second, x) { + "<" + x.first.to_string() + ", " + x.second.to_string() + ">"; } # to_string for containers -def to_string(x) : call_exists(range, x) && !x.is_type("string"){ - "[" + x.join(", ") + "]"; +def to_string(x) : call_exists(range, x) && !x.is_type("string"){ + "[" + x.join(", ") + "]"; } # Prints to console with no carriage return -def puts(x) { - print_string(x.to_string()); +def puts(x) { + print_string(x.to_string()); } # Prints to console with carriage return -def print(x) { - println_string(x.to_string()); +def print(x) { + println_string(x.to_string()); } # Returns the maximum value of two numbers def max(a, b) { - if (a>b) { - a - } else { - b - } -} + if (a>b) { + a + } else { + b + } +} # Returns the minimum value of two numbers -def min(a, b) -{ - if (a 0) && (!r.empty())) { - inserter(r.front()); - r.pop_front(); - --i; - } +def take(container, num, inserter) : call_exists(range, container) { + auto r := range(container); + auto i = num; + while ((i > 0) && (!r.empty())) { + inserter(r.front()); + r.pop_front(); + --i; + } } # Returns a new container with the given number of elements taken from the container def take(container, num) { - auto retval := new(container); - take(container, num, back_inserter(retval)); - retval; -} - - -def take_while(container, f, inserter) : call_exists(range, container) { - auto r := range(container); - while ((!r.empty()) && f(r.front())) { - inserter(r.front()); - r.pop_front(); - } -} - - -# Returns a new container with the given elements match the second value function -def take_while(container, f) { - auto retval := new(container); - take_while(container, f, back_inserter(retval)); + auto retval := new(container); + take(container, num, back_inserter(retval)); retval; } -def drop(container, num, inserter) : call_exists(range, container) { - auto r := range(container); - auto i = num; - while ((i > 0) && (!r.empty())) { - r.pop_front(); - --i; - } - while (!r.empty()) { - inserter(r.front()); - r.pop_front(); - } +def take_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty()) && f(r.front())) { + inserter(r.front()); + r.pop_front(); + } } -# Returns a new container with the given number of elements dropped from the given container +# Returns a new container with the given elements match the second value function +def take_while(container, f) { + auto retval := new(container); + take_while(container, f, back_inserter(retval)); + retval; +} + + +def drop(container, num, inserter) : call_exists(range, container) { + auto r := range(container); + auto i = num; + while ((i > 0) && (!r.empty())) { + r.pop_front(); + --i; + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } +} + + +# Returns a new container with the given number of elements dropped from the given container def drop(container, num) { - auto retval := new(container); - drop(container, num, back_inserter(retval)); - retval; + auto retval := new(container); + drop(container, num, back_inserter(retval)); + retval; } -def drop_while(container, f, inserter) : call_exists(range, container) { - auto r := range(container); - while ((!r.empty())&& f(r.front())) { - r.pop_front(); - } - while (!r.empty()) { - inserter(r.front()); - r.pop_front(); - } +def drop_while(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while ((!r.empty())&& f(r.front())) { + r.pop_front(); + } + while (!r.empty()) { + inserter(r.front()); + r.pop_front(); + } } # Returns a new container with the given elements dropped that match the second value function def drop_while(container, f) { - auto retval := new(container); - drop_while(container, f, back_inserter(retval)); - retval; + auto retval := new(container); + drop_while(container, f, back_inserter(retval)); + retval; } # Applies the second value function to the container. Starts with the first two elements. Expects at least 2 elements. -def reduce(container, func) : container.size() >= 2 && call_exists(range, container) { - auto r := range(container); - auto retval = r.front(); - r.pop_front(); - retval = func(retval, r.front()); - r.pop_front(); - while (!r.empty()) { - retval = func(retval, r.front()); - r.pop_front(); - } - retval; +def reduce(container, func) : container.size() >= 2 && call_exists(range, container) { + auto r := range(container); + auto retval = r.front(); + r.pop_front(); + retval = func(retval, r.front()); + r.pop_front(); + while (!r.empty()) { + retval = func(retval, r.front()); + r.pop_front(); + } + retval; } # Returns a string of the elements in container delimited by the second value string -def join(container, delim) { - auto retval = ""; - auto range := range(container); - if (!range.empty()) { - retval += to_string(range.front()); - range.pop_front(); - while (!range.empty()) { - retval += delim; - retval += to_string(range.front()); - range.pop_front(); - } - } - retval; +def join(container, delim) { + auto retval = ""; + auto range := range(container); + if (!range.empty()) { + retval += to_string(range.front()); + range.pop_front(); + while (!range.empty()) { + retval += delim; + retval += to_string(range.front()); + range.pop_front(); + } + } + retval; } -def filter(container, f, inserter) : call_exists(range, container) { - auto r := range(container); - while (!r.empty()) { - if (f(r.front())) { - inserter(r.front()); - } - r.pop_front(); - } -} +def filter(container, f, inserter) : call_exists(range, container) { + auto r := range(container); + while (!r.empty()) { + if (f(r.front())) { + inserter(r.front()); + } + r.pop_front(); + } +} # Returns a new Vector which match the second value function -def filter(container, f) { - auto retval := new(container); +def filter(container, f) { + auto retval := new(container); filter(container, f, back_inserter(retval)); retval; } -def generate_range(x, y, inserter) { - auto i = x; - while (i <= y) { - inserter(i); - ++i; - } +def generate_range(x, y, inserter) { + auto i = x; + while (i <= y) { + inserter(i); + ++i; + } } # Returns a new Vector which represents the range from the first value to the second value -def generate_range(x, y) { - auto retval := Vector(); - generate_range(x,y,back_inserter(retval)); - retval; +def generate_range(x, y) { + auto retval := Vector(); + generate_range(x,y,back_inserter(retval)); + retval; } # Returns a new Vector with the first value to the second value as its elements -def collate(x, y) { - return [x, y]; +def collate(x, y) { + return [x, y]; } -def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) { - auto r_x := range(x); - auto r_y := range(y); - while (!r_x.empty() && !r_y.empty()) { - inserter(f(r_x.front(), r_y.front())); - r_x.pop_front(); - r_y.pop_front(); - } +def zip_with(f, x, y, inserter) : call_exists(range, x) && call_exists(range, y) { + auto r_x := range(x); + auto r_y := range(y); + while (!r_x.empty() && !r_y.empty()) { + inserter(f(r_x.front(), r_y.front())); + r_x.pop_front(); + r_y.pop_front(); + } } # Returns a new Vector which joins matching elements of the second and third value with the first value function -def zip_with(f, x, y) { - auto retval := Vector(); - zip_with(f,x,y,back_inserter(retval)); +def zip_with(f, x, y) { + auto retval := Vector(); + zip_with(f,x,y,back_inserter(retval)); retval; } # Returns a new Vector which joins matching elements of the first and second -def zip(x, y) { - zip_with(collate, x, y); +def zip(x, y) { + zip_with(collate, x, y); } # Returns the position of the second value string in the first value string -def string::find(string substr) { - find(this, substr, size_t(0)); +def string::find(string substr) { + find(this, substr, size_t(0)); } # Returns the position of last match of the second value string in the first value string -def string::rfind(string substr) { - rfind(this, substr, size_t(-1)); +def string::rfind(string substr) { + rfind(this, substr, size_t(-1)); } # Returns the position of the first match of elements in the second value string in the first value string -def string::find_first_of(string list) { - find_first_of(this, list, size_t(0)); -} +def string::find_first_of(string list) { + find_first_of(this, list, size_t(0)); +} # Returns the position of the last match of elements in the second value string in the first value string def string::find_last_of(string list) { - find_last_of(this, list, size_t(-1)); -} + find_last_of(this, list, size_t(-1)); +} # Returns the position of the first non-matching element in the second value string in the first value string -def string::find_first_not_of(string list) { - find_first_not_of(this, list, size_t(0)); -} +def string::find_first_not_of(string list) { + find_first_not_of(this, list, size_t(0)); +} # Returns the position of the last non-matching element in the second value string in the first value string -def string::find_last_not_of(string list) { - find_last_not_of(this, list, size_t(-1)); -} - - -def string::ltrim() { - drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}); -} - - -def string::rtrim() { - reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'})); -} - - -def string::trim() { - ltrim(rtrim(this)); +def string::find_last_not_of(string list) { + find_last_not_of(this, list, size_t(-1)); } -def find(container, value, Function compare_func) : call_exists(range, container) { - auto range := range(container); - while (!range.empty()) { - if (compare_func(range.front(), value)) { - return range; - } else { - range.pop_front(); - } - } - range; -} +def string::ltrim() { + drop_while(this, fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'}); +} -def find(container, value) { - find(container, value, eq) -} +def string::rtrim() { + reverse(drop_while(reverse(this), fun(x) { x == ' ' || x == '\t' || x == '\r' || x == '\n'})); +} + + +def string::trim() { + ltrim(rtrim(this)); +} + + +def find(container, value, Function compare_func) : call_exists(range, container) { + auto range := range(container); + while (!range.empty()) { + if (compare_func(range.front(), value)) { + return range; + } else { + range.pop_front(); + } + } + range; +} + + +def find(container, value) { + find(container, value, eq) +} )chaiscript"; -} - -}; -} + } + }; +} // namespace chaiscript #endif /* CHAISCRIPT_PRELUDE_HPP_ */ diff --git a/include/chaiscript/language/chaiscript_prelude_docs.hpp b/include/chaiscript/language/chaiscript_prelude_docs.hpp index 7df56521..c7fe40c7 100644 --- a/include/chaiscript/language/chaiscript_prelude_docs.hpp +++ b/include/chaiscript/language/chaiscript_prelude_docs.hpp @@ -2,26 +2,22 @@ /// regarding the ChaiScript standard runtime library. /// \brief Items in this namespace exist in the ChaiScript language runtime. They are not part of the C++ API -namespace ChaiScript_Language -{ +namespace ChaiScript_Language { + /// \page LangStandardLibraryRef ChaiScript Language Standard Library Reference + /// + /// ChaiScript, at its core, has some very functional programming-inspired habits. Few places show this off as clearly + /// as the prelude, itself a name taken as a nod to the popular functional language Haskell. This prelude is available + /// to all standard ChaiScript applications, and provides a simple foundation for using numbers, strings, and ranges + /// (the general category of Range cs and their iteration). + /// -/// \page LangStandardLibraryRef ChaiScript Language Standard Library Reference -/// -/// ChaiScript, at its core, has some very functional programming-inspired habits. Few places show this off as clearly -/// as the prelude, itself a name taken as a nod to the popular functional language Haskell. This prelude is available -/// to all standard ChaiScript applications, and provides a simple foundation for using numbers, strings, and ranges -/// (the general category of Range cs and their iteration). -/// - - -/// \brief Generic concept of a value in ChaiScript. -/// -/// The Object type exists merely as a concept. All objects in ChaiScript support this concept -/// and have the following methods available to them. All objects are stored internally as chaiscript::Boxed_Value types. -/// -/// \sa chaiscript::Boxed_Value -class Object -{ + /// \brief Generic concept of a value in ChaiScript. + /// + /// The Object type exists merely as a concept. All objects in ChaiScript support this concept + /// and have the following methods available to them. All objects are stored internally as chaiscript::Boxed_Value types. + /// + /// \sa chaiscript::Boxed_Value + class Object { public: /// \brief Returns the Type_Info value for this Object Type_Info get_type_info() const; @@ -41,52 +37,49 @@ class Object /// \brief Returns true if the Object is stored as a pointer bool is_var_pointer() const; - /// \brief Returns true if the Object is stored as a reference + /// \brief Returns true if the Object is stored as a reference bool is_var_reference() const; /// \brief Returns true if the Object does not contain a value is is undefined. bool is_var_undef() const; - /// \brief Returns the registered name of the type of the object. + /// \brief Returns the registered name of the type of the object. /// /// \sa Type_Info::name(); string type_name() const; -}; + }; -/// \brief Item returned from a Range object from a Map -class Map_Pair -{ + /// \brief Item returned from a Range object from a Map + class Map_Pair { public: /// \brief Returns the key of the Map entry const string first(); - /// \brief Returns the value Object of the Map entry + /// \brief Returns the value Object of the Map entry Object second(); -}; + }; - -/// \brief Maps strings to Objects -/// -/// ChaiScript has a built in shortcut for generating Map objects: -/// -/// Example: -/// \code -/// eval> var m = ["a":1, "b":2]; -/// [, ] -/// eval> m.count("a"); -/// 1 -/// eval> m.count("c"); -/// 0 -/// eval> m.size(); -/// 2 -/// \endcode -/// -/// Implemented as std::map -/// -/// \sa Map_Pair -/// \sa chaiscript::bootstrap::standard_library::map_type -class Map -{ + /// \brief Maps strings to Objects + /// + /// ChaiScript has a built in shortcut for generating Map objects: + /// + /// Example: + /// \code + /// eval> var m = ["a":1, "b":2]; + /// [, ] + /// eval> m.count("a"); + /// 1 + /// eval> m.count("c"); + /// 0 + /// eval> m.size(); + /// 2 + /// \endcode + /// + /// Implemented as std::map + /// + /// \sa Map_Pair + /// \sa chaiscript::bootstrap::standard_library::map_type + class Map { public: /// \brief Returns an object that implements the Range concept for the Map_Pair's in this Map Range range(); @@ -108,64 +101,57 @@ class Map /// \brief Returns true if the map contains no items bool empty() const; + }; -}; - - -/// \brief A concept implemented by string, Vector and Map. It is convertible to Range, default constructable and back_insertable -class Container -{ + /// \brief A concept implemented by string, Vector and Map. It is convertible to Range, default constructable and back_insertable + class Container { public: void push_back(Object); Range range(); Const_Range range() const; -}; + }; + /// \brief Converts o into a string. + /// + /// \code + /// eval> to_string(3).is_type("string")
+ /// true
+ /// \endcode + string to_string(Object o); -/// \brief Converts o into a string. -/// -/// \code -/// eval> to_string(3).is_type("string")
-/// true
-/// \endcode -string to_string(Object o); + /// \brief Prints o to the terminal, without a trailing carriage return. Applies conversions to string automatically. + /// \code + /// eval> puts("hi, "); puts("there") + /// hi, thereeval> + /// \endcode + /// \sa to_string + /// \sa print + void puts(Object o); + /// \brief Prints o to the terminal, with a trailing carriage return. Applies conversions to string automatically + /// \code + /// eval> print("hello") + /// hello + /// eval> + /// \endcode + /// \sa to_string + /// \sa puts + void print(Object o); -/// \brief Prints o to the terminal, without a trailing carriage return. Applies conversions to string automatically. -/// \code -/// eval> puts("hi, "); puts("there") -/// hi, thereeval> -/// \endcode -/// \sa to_string -/// \sa print -void puts(Object o); - - -/// \brief Prints o to the terminal, with a trailing carriage return. Applies conversions to string automatically -/// \code -/// eval> print("hello") -/// hello -/// eval> -/// \endcode -/// \sa to_string -/// \sa puts -void print(Object o); - -/// \brief ChaiScript representation of std::string. It is an std::string but only some member are exposed to ChaiScript. -/// -/// Because the ChaiScript string object is an std::string, it is directly convertible to and from std::string -/// using the chaiscript::boxed_cast and chaiscript::var functions. -/// -/// With the exception of string::trim, string::rtrim, string::ltrim, all members are direct pass-throughs to the -/// std::string of the same name. -/// -/// \note Object and function notations are equivalent in ChaiScript. This means that -/// \c "bob".find("b") and \c find("bob", "b") are exactly the same. Most examples below follow the -/// second formation of the function calls. -/// \sa \ref keyworddef for extending existing C++ classes in ChaiScript -/// \sa chaiscript::bootstrap::standard_library::string_type -class string -{ + /// \brief ChaiScript representation of std::string. It is an std::string but only some member are exposed to ChaiScript. + /// + /// Because the ChaiScript string object is an std::string, it is directly convertible to and from std::string + /// using the chaiscript::boxed_cast and chaiscript::var functions. + /// + /// With the exception of string::trim, string::rtrim, string::ltrim, all members are direct pass-throughs to the + /// std::string of the same name. + /// + /// \note Object and function notations are equivalent in ChaiScript. This means that + /// \c "bob".find("b") and \c find("bob", "b") are exactly the same. Most examples below follow the + /// second formation of the function calls. + /// \sa \ref keyworddef for extending existing C++ classes in ChaiScript + /// \sa chaiscript::bootstrap::standard_library::string_type + class string { public: /// \brief Finds the first instance of substr. /// \code @@ -174,7 +160,6 @@ class string /// \endcode int find(string s) const; - /// \brief Finds the last instance of substr. /// \code /// eval> rfind("abab", "ab") @@ -211,7 +196,7 @@ class string /// \code /// eval> find_last_not_of("abcd", "fec") /// 3 - /// \endcode + /// \endcode int find_last_not_of(string list) const; /// \brief Removes whitespace from the front of the string, returning a new string @@ -246,7 +231,7 @@ class string /// eval> trim(" bob ") + "|" /// bob| /// \endcode - /// + /// /// Equivalent to rtrim(ltrim(" bob ")); /// /// \sa \ref keyworddef @@ -281,16 +266,15 @@ class string /// \brief Returns an object that implements the Const_Range concept for the characters of this string Const_Range range() const; -}; + }; -/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides -/// easy iteration over the elements in a container. -/// -/// Implemented by the template chaiscript::bootstrap::standard_library::Bidir_Range -/// -/// \sa Const_Range -class Range -{ + /// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides + /// easy iteration over the elements in a container. + /// + /// Implemented by the template chaiscript::bootstrap::standard_library::Bidir_Range + /// + /// \sa Const_Range + class Range { public: /// \brief Returns the last item of the range Object back(); @@ -303,25 +287,23 @@ class Range Object front(); /// \brief Moves the back pointer back one. - /// + /// /// \post back() returns the element at back() - 1; void pop_back(); /// \brief Moves the front pointer forward one - /// + /// /// \post front() returns the element at front() + 1; void pop_front(); + }; -}; - -/// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides -/// easy iteration over the elements in a container. Contained values are const. -/// -/// Implemented by the template chaiscript::bootstrap::standard_library::Const_Bidir_Range -/// -/// \sa Range -class Const_Range -{ + /// \brief A concept in ChaiScript that is implemented by \ref string, Vector and Map. It provides + /// easy iteration over the elements in a container. Contained values are const. + /// + /// Implemented by the template chaiscript::bootstrap::standard_library::Const_Bidir_Range + /// + /// \sa Range + class Const_Range { public: /// \brief Returns the last item of the range const Object back(); @@ -334,36 +316,34 @@ class Const_Range const Object front(); /// \brief Moves the back pointer back one. - /// + /// /// \post back() returns the element at back() - 1; void pop_back(); /// \brief Moves the front pointer forward one - /// + /// /// \post front() returns the element at front() + 1; void pop_front(); + }; -}; - -/// \brief A vector of Objects -/// -/// ChaiScript includes a shortcut for creating a Vector of Objects -/// -/// Example: -/// \code -/// eval> var v = [1,2,3,4] -/// [1, 2, 3, 4] -/// eval> v[0]; -/// 1 -/// eval> v.size(); -/// 4 -/// \endcode -/// -/// Implemented with std::vector -/// -/// \sa chaiscript::bootstrap::standard_library::vector_type -class Vector -{ + /// \brief A vector of Objects + /// + /// ChaiScript includes a shortcut for creating a Vector of Objects + /// + /// Example: + /// \code + /// eval> var v = [1,2,3,4] + /// [1, 2, 3, 4] + /// eval> v[0]; + /// 1 + /// eval> v.size(); + /// 4 + /// \endcode + /// + /// Implemented with std::vector + /// + /// \sa chaiscript::bootstrap::standard_library::vector_type + class Vector { public: /// \brief returns the Object at the given index. Throws an exception if the index does not exist Object operator[](int t_index); @@ -387,15 +367,15 @@ class Vector Object front(); /// \brief Inserts a new item in the Vector at the given index. The item is not cloned on insert - /// + /// /// \sa insert_ref void insert_ref_at(int, Object); /// \brief Inserts a new item in the Vector at the given index. The item is cloned on insert - /// + /// /// \sa insert_ref void insert_at(int, Object); - + /// \brief Removes the last item from the Vector void pop_back(); @@ -417,11 +397,9 @@ class Vector /// \brief Returns the number of elements in the Vector int size() const; + }; -}; - -class Type_Info -{ + class Type_Info { public: /// \brief Compares this Type_Info object with another one and returns true if the two types are the same /// after const, pointer, reference are removed. @@ -439,10 +417,10 @@ class Type_Info /// \brief Returns true if the type is a pointer bool is_type_pointer() const; - /// \brief Returns true if the type is a reference + /// \brief Returns true if the type is a reference bool is_type_reference() const; - /// \brief Returns true if the type is undefined + /// \brief Returns true if the type is undefined bool is_type_undef() const; /// \brief Returns true if the type is "void" @@ -450,32 +428,29 @@ class Type_Info /// \brief Returns the ChaiScript registered name for the type if one exists. string name() const; + }; -}; - - -/// \brief Represents a function object in ChaiScript -/// -/// A function object may be one function, such as: -/// \code -/// var f = fun(x) { return x; } -/// \endcode -/// -/// Or it may represent multiple functions -/// \code -/// var f2 = `-`; // represents the unary - as well as the set of binary - operators -/// \endcode -/// -/// Guarded function example -/// \code -/// def f3(x) : x > 2 { -/// return x; -/// } -/// \endcode -/// -/// Examples in the function definitions below will reference these examples -class Function -{ + /// \brief Represents a function object in ChaiScript + /// + /// A function object may be one function, such as: + /// \code + /// var f = fun(x) { return x; } + /// \endcode + /// + /// Or it may represent multiple functions + /// \code + /// var f2 = `-`; // represents the unary - as well as the set of binary - operators + /// \endcode + /// + /// Guarded function example + /// \code + /// def f3(x) : x > 2 { + /// return x; + /// } + /// \endcode + /// + /// Examples in the function definitions below will reference these examples + class Function { public: /// \brief Returns the annotation description of the function string get_annotation() const; @@ -492,7 +467,7 @@ class Function int get_arity() const; /// \brief Returns a vector of the contained functions - /// + /// /// Example: /// \code /// eval> f.get_contained_functions().size() @@ -506,7 +481,7 @@ class Function Vector get_contained_functions() const; /// \brief Returns a function guard as function - /// + /// /// Example: /// \code /// eval> f.get_guard() // Throws exception @@ -518,12 +493,12 @@ class Function /// \brief Returns a vector of Type_Info objects that represent the param types for this function. /// The first value in the list is the return type. - /// + /// /// If this function is a conglomerate of several functions (get_contained_values().size() > 0) /// then the function returns as many Type_Info objects as it can. If the functions contained all have /// the same arity, then it represents the arity. If they have different arities, it returns only - /// one value - the return type. - /// + /// one value - the return type. + /// /// For each parameter that is the same type, the type is returned. If the types are different /// then a Type_Info for Object is returned. /// @@ -538,293 +513,271 @@ class Function bool has_guard() const; /// \brief Calls the function with the given set of parameters and returns the value; - /// + /// /// Example: /// \code /// eval> `-`.call([2,1]); /// 1 /// \endcode Object call(Vector t_params) const; -} + } + /// \brief Returns the max of a or b. Requires that operator>(a, b) exists + /// Equivalent to + /// \code + /// return a>b?a:b; + /// \endcode + /// + /// Example: + /// \code + /// eval> max(4, 10) + /// 10 + /// \endcode + Object + max(Object a, Object b); + /// \brief Returns the min of a or b. Requires that operator<(a, b) exists + /// + /// Equivalent to + /// \code + /// return a min(4, 10) + /// 4 + /// \endcode + Object min(Object a, Object b); -/// \brief Returns the max of a or b. Requires that operator>(a, b) exists -/// Equivalent to -/// \code -/// return a>b?a:b; -/// \endcode -/// -/// Example: -/// \code -/// eval> max(4, 10) -/// 10 -/// \endcode -Object max(Object a, Object b); + /// \brief Returns true if x is an even integer. + /// + /// Will also work on any non-integer type for which an operator%(x, int) exists + /// + /// Example: + /// \code + /// eval> even(4) + /// true + /// \endcode + bool even(Object x); -/// \brief Returns the min of a or b. Requires that operator<(a, b) exists -/// -/// Equivalent to -/// \code -/// return a min(4, 10) -/// 4 -/// \endcode -Object min(Object a, Object b); + /// \brief Returns true if x is an odd integer. + /// + /// Will also work on any non-integer type for which an operator%(x, int) exists + /// + /// Example: + /// \code + /// eval> odd(4) + /// false + /// \endcode + bool even(Object x); -/// \brief Returns true if x is an even integer. -/// -/// Will also work on any non-integer type for which an operator%(x, int) exists -/// -/// Example: -/// \code -/// eval> even(4) -/// true -/// \endcode -bool even(Object x); + /// \brief Applies the function f over each element in the Range c. + /// + /// Example: + /// \code + /// eval> for_each([1, 2, 3], print) + /// 1 + /// 2 + /// 3 + /// \endcode + void for_each(Range c, Function f); -/// \brief Returns true if x is an odd integer. -/// -/// Will also work on any non-integer type for which an operator%(x, int) exists -/// -/// Example: -/// \code -/// eval> odd(4) -/// false -/// \endcode -bool even(Object x); + /// \brief Applies f over each element in the Range c, joining all the results. + /// + /// Example: + /// \code + /// eval> map([1, 2, 3], odd) + /// [true, false, true] + /// \endcode + Object map(Range c, Function f); + /// \brief Starts with the initial value and applies the function f to it and the first element of the Range c. + /// The result is then applied to the second element, and so on until the elements are exhausted. + /// + /// Example: + /// \code + /// eval> foldl([1, 2, 3, 4], `+`, 0) + /// 10 + /// \endcode + Object foldl(Range c, Function f, Object initial); -/// \brief Applies the function f over each element in the Range c. -/// -/// Example: -/// \code -/// eval> for_each([1, 2, 3], print) -/// 1 -/// 2 -/// 3 -/// \endcode -void for_each(Range c, Function f); + /// \brief Returns the sum total of the values in the Range c. + /// + /// Example: + /// \code + /// eval> sum([1, 2, 3, 4]) + /// 10 + /// \endcode + /// + /// Equivalent to: + /// \code + /// foldl(c, `+`, 0.0); + /// \endcode + Numeric sum(Range c); + /// \brief Returns the product of the value in the Range c. + /// + /// Example: + /// \code + /// eval> product([1, 2, 3, 4]) + /// 24 + /// \endcode + /// + /// Equivalent to: + /// \code + /// foldl(c, `*`, 1.0); + /// \endcode + Numeric product(Range c); -/// \brief Applies f over each element in the Range c, joining all the results. -/// -/// Example: -/// \code -/// eval> map([1, 2, 3], odd) -/// [true, false, true] -/// \endcode -Object map(Range c, Function f); + /// \brief Takes num elements from the Range c, returning them. + /// + /// Example: + /// \code + /// eval> take([1, 2, 3, 4], 2) + /// [1, 2] + /// \endcode + /// + /// \returns A container of the same type that was passed in + Object take(Range c, int num); + /// \brief Takes elements from the Range c that match function f, stopping at the first non-match, returning them as a new Vector. + /// + /// Example: + /// \code + /// eval> take_while([1, 2, 3], odd) + /// [1] + /// \endcode + /// + /// \returns A container of the same type that was passed in + Object take_while(Range c, Function f); -/// \brief Starts with the initial value and applies the function f to it and the first element of the Range c. -/// The result is then applied to the second element, and so on until the elements are exhausted. -/// -/// Example: -/// \code -/// eval> foldl([1, 2, 3, 4], `+`, 0) -/// 10 -/// \endcode -Object foldl(Range c, Function f, Object initial); + /// \brief Drops num elements from the Range c, returning the remainder. + /// + /// Example: + /// \code + /// eval> drop([1, 2, 3, 4], 2) + /// [3, 4] + /// \endcode + /// + /// \returns A container of the same type that was passed in + Object drop(Range c, int num); + /// \brief Drops elements from the Range c that match f, stopping at the first non-match, returning the remainder. + /// + /// Example: + /// \code + /// eval> drop_while([1, 2, 3], odd) + /// [2, 3] + /// \endcode + Object drop_while(Range c, Function f); -/// \brief Returns the sum total of the values in the Range c. -/// -/// Example: -/// \code -/// eval> sum([1, 2, 3, 4]) -/// 10 -/// \endcode -/// -/// Equivalent to: -/// \code -/// foldl(c, `+`, 0.0); -/// \endcode -Numeric sum(Range c); + /// \brief Similar to foldl, this takes the first two elements as its starting values for f. This assumes Range c has at least 2 elements. + /// + /// Example: + /// \code + /// eval> reduce([1, 2, 3, 4], `+`) + /// 10 + /// \endcode + Object reduce(Range c, Function f); + /// \brief Takes elements from Container c that match function f, return them. + /// + /// Example: + /// \code + /// eval> filter([1, 2, 3, 4], odd) + /// [1, 3] + /// \endcode + Object filter(Container c, Function f); -/// \brief Returns the product of the value in the Range c. -/// -/// Example: -/// \code -/// eval> product([1, 2, 3, 4]) -/// 24 -/// \endcode -/// -/// Equivalent to: -/// \code -/// foldl(c, `*`, 1.0); -/// \endcode -Numeric product(Range c); + /// \brief Joins the elements of the Range c into a string, delimiting each with the delim string. + /// + /// Example: + /// \code + /// eval> join([1, 2, 3], "*") + /// 1*2*3 + /// \endcode + string join(Range c, string delim); + /// \brief Returns the contents of the Container c in reversed order. + /// + /// Example: + /// \code + /// eval> reverse([1, 2, 3, 4, 5, 6, 7]) + /// [7, 6, 5, 4, 3, 2, 1] + /// \endcode + Container reverse(Container c); -/// \brief Takes num elements from the Range c, returning them. -/// -/// Example: -/// \code -/// eval> take([1, 2, 3, 4], 2) -/// [1, 2] -/// \endcode -/// -/// \returns A container of the same type that was passed in -Object take(Range c, int num); + /// \brief Generates a new Vector filled with values starting at x and ending with y. + /// + /// Works on types supporting operator<=(x, y) and operator++(x) + /// + /// Example: + /// \code + /// eval> generate_range(1, 10) + /// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + /// \endcode + Vector generate_range(Object x, Object y); + /// \brief Returns a new Range with x and y concatenated. + /// + /// Example: + /// \code + /// eval> concat([1, 2, 3], [4, 5, 6]) + /// [1, 2, 3, 4, 5, 6] + /// \endcode + Object concat(Range x, Range y); -/// \brief Takes elements from the Range c that match function f, stopping at the first non-match, returning them as a new Vector. -/// -/// Example: -/// \code -/// eval> take_while([1, 2, 3], odd) -/// [1] -/// \endcode -/// -/// \returns A container of the same type that was passed in -Object take_while(Range c, Function f); + /// \brief Returns a new Vector with x and y as its values. + /// + /// Example: + /// \code + /// eval> collate(1, 2) + /// [1, 2] + /// \endcode + Vector collate(Object x, Object y); + /// \brief Applies f to elements of x and y, returning a new Vector with the result of each application. + /// + /// Example: + /// \code + /// eval> zip_with(`+`, [1, 2, 3], [4, 5, 6]) + /// [5, 7, 9] + /// \endcode + Vector zip_with(Function f, Range x, Range y); -/// \brief Drops num elements from the Range c, returning the remainder. -/// -/// Example: -/// \code -/// eval> drop([1, 2, 3, 4], 2) -/// [3, 4] -/// \endcode -/// -/// \returns A container of the same type that was passed in -Object drop(Range c, int num); + /// \brief Collates elements of x and y, returning a new Vector with the result. + /// + /// Example: + /// \code + /// eval> zip([1, 2, 3], [4, 5, 6]) + /// [[1, 4], [2, 5], [3, 6]] + /// \endcode + Vector zip(Range x, Range y); + /// \brief returns true if there exists a call to the Function f that takes the given parameters + /// + /// Example: + /// \code + /// eval> call_exists(`+`, 1, 2) + /// true + /// \endcode + bool call_exists(Function f, ...); -/// \brief Drops elements from the Range c that match f, stopping at the first non-match, returning the remainder. -/// -/// Example: -/// \code -/// eval> drop_while([1, 2, 3], odd) -/// [2, 3] -/// \endcode -Object drop_while(Range c, Function f); + /// \brief Reverses a Range object so that the elements are accessed in reverse + Range retro(Range); + /// \brief Reverses a Const_Range object so that the elements are accessed in reverse + Const_Range retro(Const_Range); -/// \brief Similar to foldl, this takes the first two elements as its starting values for f. This assumes Range c has at least 2 elements. -/// -/// Example: -/// \code -/// eval> reduce([1, 2, 3, 4], `+`) -/// 10 -/// \endcode -Object reduce(Range c, Function f); - - -/// \brief Takes elements from Container c that match function f, return them. -/// -/// Example: -/// \code -/// eval> filter([1, 2, 3, 4], odd) -/// [1, 3] -/// \endcode -Object filter(Container c, Function f); - - -/// \brief Joins the elements of the Range c into a string, delimiting each with the delim string. -/// -/// Example: -/// \code -/// eval> join([1, 2, 3], "*") -/// 1*2*3 -/// \endcode -string join(Range c, string delim); - - -/// \brief Returns the contents of the Container c in reversed order. -/// -/// Example: -/// \code -/// eval> reverse([1, 2, 3, 4, 5, 6, 7]) -/// [7, 6, 5, 4, 3, 2, 1] -/// \endcode -Container reverse(Container c); - - -/// \brief Generates a new Vector filled with values starting at x and ending with y. -/// -/// Works on types supporting operator<=(x, y) and operator++(x) -/// -/// Example: -/// \code -/// eval> generate_range(1, 10) -/// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] -/// \endcode -Vector generate_range(Object x, Object y); - - -/// \brief Returns a new Range with x and y concatenated. -/// -/// Example: -/// \code -/// eval> concat([1, 2, 3], [4, 5, 6]) -/// [1, 2, 3, 4, 5, 6] -/// \endcode -Object concat(Range x, Range y); - - -/// \brief Returns a new Vector with x and y as its values. -/// -/// Example: -/// \code -/// eval> collate(1, 2) -/// [1, 2] -/// \endcode -Vector collate(Object x, Object y); - - -/// \brief Applies f to elements of x and y, returning a new Vector with the result of each application. -/// -/// Example: -/// \code -/// eval> zip_with(`+`, [1, 2, 3], [4, 5, 6]) -/// [5, 7, 9] -/// \endcode -Vector zip_with(Function f, Range x, Range y); - - -/// \brief Collates elements of x and y, returning a new Vector with the result. -/// -/// Example: -/// \code -/// eval> zip([1, 2, 3], [4, 5, 6]) -/// [[1, 4], [2, 5], [3, 6]] -/// \endcode -Vector zip(Range x, Range y); - - -/// \brief returns true if there exists a call to the Function f that takes the given parameters -/// -/// Example: -/// \code -/// eval> call_exists(`+`, 1, 2) -/// true -/// \endcode -bool call_exists(Function f, ...); - -/// \brief Reverses a Range object so that the elements are accessed in reverse -Range retro(Range); - -/// \brief Reverses a Const_Range object so that the elements are accessed in reverse -Const_Range retro(Const_Range); - - -/// \brief Raises the given object as an exception. Any type of object can be thrown. -/// -/// Example: -/// \code -/// eval> try { throw(1); } catch (e) { print("Exception caught: " + to_string(e)); } -/// Exception caught: 1 -/// \endcode -/// -/// \sa \ref keywordtry -void throw(Object); -} - + /// \brief Raises the given object as an exception. Any type of object can be thrown. + /// + /// Example: + /// \code + /// eval> try { throw(1); } catch (e) { print("Exception caught: " + to_string(e)); } + /// Exception caught: 1 + /// \endcode + /// + /// \sa \ref keywordtry + void throw(Object); +} // namespace ChaiScript_Language diff --git a/include/chaiscript/language/chaiscript_tracer.hpp b/include/chaiscript/language/chaiscript_tracer.hpp index 9d111815..c265dadd 100644 --- a/include/chaiscript/language/chaiscript_tracer.hpp +++ b/include/chaiscript/language/chaiscript_tracer.hpp @@ -1,46 +1,37 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_TRACER_HPP_ #define CHAISCRIPT_TRACER_HPP_ -namespace chaiscript { - namespace eval { +namespace chaiscript::eval { + struct Noop_Tracer_Detail { + template + constexpr void trace(const chaiscript::detail::Dispatch_State &, const AST_Node_Impl *) noexcept { + } + }; + template + struct Tracer : T... { + Tracer() = default; + constexpr explicit Tracer(T... t) + : T(std::move(t))... { + } - struct Noop_Tracer_Detail - { - template - void trace(const chaiscript::detail::Dispatch_State &, const AST_Node_Impl *) - { - } - }; + void do_trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { + (static_cast(*this).trace(ds, node), ...); + } - template - struct Tracer : T... - { - Tracer() = default; - explicit Tracer(T ... t) - : T(std::move(t))... - { - } + static void trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { + ds->get_parser().get_tracer>().do_trace(ds, node); + } + }; - void do_trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { - (void)std::initializer_list{ (static_cast(*this).trace(ds, node), 0)... }; - } + using Noop_Tracer = Tracer; - static void trace(const chaiscript::detail::Dispatch_State &ds, const AST_Node_Impl> *node) { - ds->get_parser().get_tracer>().do_trace(ds, node); - } - }; - - typedef Tracer Noop_Tracer; - - } -} +} // namespace chaiscript::eval #endif - diff --git a/include/chaiscript/language/chaiscript_unknown.hpp b/include/chaiscript/language/chaiscript_unknown.hpp index 8fc1d494..e2a64707 100644 --- a/include/chaiscript/language/chaiscript_unknown.hpp +++ b/include/chaiscript/language/chaiscript_unknown.hpp @@ -1,21 +1,16 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_UNKNOWN_HPP_ #define CHAISCRIPT_UNKNOWN_HPP_ - -namespace chaiscript -{ - namespace detail - { - struct Loadable_Module - { - Loadable_Module(const std::string &, const std::string &) - { +namespace chaiscript { + namespace detail { + struct Loadable_Module { + Loadable_Module(const std::string &, const std::string &) { #ifdef CHAISCRIPT_NO_DYNLOAD throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)"); #else @@ -25,7 +20,6 @@ namespace chaiscript ModulePtr m_moduleptr; }; - } -} + } // namespace detail +} // namespace chaiscript #endif - diff --git a/include/chaiscript/language/chaiscript_windows.hpp b/include/chaiscript/language/chaiscript_windows.hpp index 493572c2..5e2b6e88 100644 --- a/include/chaiscript/language/chaiscript_windows.hpp +++ b/include/chaiscript/language/chaiscript_windows.hpp @@ -1,7 +1,7 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_WINDOWS_HPP_ @@ -17,42 +17,33 @@ #include #endif - -namespace chaiscript -{ - namespace detail - { - struct Loadable_Module - { +namespace chaiscript { + namespace detail { + struct Loadable_Module { template - static std::wstring to_wstring(const T &t_str) - { - return std::wstring(t_str.begin(), t_str.end()); - } + static std::wstring to_wstring(const T &t_str) { + return std::wstring(t_str.begin(), t_str.end()); + } template - static std::string to_string(const T &t_str) - { - return std::string(t_str.begin(), t_str.end()); - } + static std::string to_string(const T &t_str) { + return std::string(t_str.begin(), t_str.end()); + } #if defined(_UNICODE) || defined(UNICODE) - template - static std::wstring to_proper_string(const T &t_str) - { - return to_wstring(t_str); - } + template + static std::wstring to_proper_string(const T &t_str) { + return to_wstring(t_str); + } #else template - static std::string to_proper_string(const T &t_str) - { - return to_string(t_str); - } + static std::string to_proper_string(const T &t_str) { + return to_string(t_str); + } #endif - static std::string get_error_message(DWORD t_err) - { - typedef LPTSTR StringType; + static std::string get_error_message(DWORD t_err) { + using StringType = LPTSTR; #if defined(_UNICODE) || defined(UNICODE) std::wstring retval = L"Unknown Error"; @@ -61,16 +52,15 @@ namespace chaiscript #endif StringType lpMsgBuf = nullptr; - if (FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - t_err, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - reinterpret_cast(&lpMsgBuf), - 0, nullptr ) != 0 && lpMsgBuf) - { + if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + t_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&lpMsgBuf), + 0, + nullptr) + != 0 + && lpMsgBuf) { retval = lpMsgBuf; LocalFree(lpMsgBuf); } @@ -78,13 +68,10 @@ namespace chaiscript return to_string(retval); } - struct DLModule - { + struct DLModule { explicit DLModule(const std::string &t_filename) - : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) - { - if (!m_data) - { + : m_data(LoadLibrary(to_proper_string(t_filename).c_str())) { + if (!m_data) { throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); } } @@ -94,40 +81,33 @@ namespace chaiscript DLModule(const DLModule &) = delete; DLModule &operator=(const DLModule &) = delete; - ~DLModule() - { - FreeLibrary(m_data); - } + ~DLModule() { FreeLibrary(m_data); } HMODULE m_data; }; template - struct DLSym - { - DLSym(DLModule &t_mod, const std::string &t_symbol) - : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) - { - if (!m_symbol) - { - throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); - } + struct DLSym { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(GetProcAddress(t_mod.m_data, t_symbol.c_str()))) { + if (!m_symbol) { + throw chaiscript::exception::load_module_error(get_error_message(GetLastError())); } + } - T m_symbol; - }; + T m_symbol; + }; Loadable_Module(const std::string &t_module_name, const std::string &t_filename) - : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name), - m_moduleptr(m_func.m_symbol()) - { + : m_dlmodule(t_filename) + , m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name) + , m_moduleptr(m_func.m_symbol()) { } DLModule m_dlmodule; DLSym m_func; ModulePtr m_moduleptr; }; - } -} -#endif - + } // namespace detail +} // namespace chaiscript +#endif diff --git a/include/chaiscript/utility/fnv1a.hpp b/include/chaiscript/utility/fnv1a.hpp index 9e549928..7ebd12dc 100644 --- a/include/chaiscript/utility/fnv1a.hpp +++ b/include/chaiscript/utility/fnv1a.hpp @@ -1,25 +1,17 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_UTILITY_FNV1A_HPP_ #define CHAISCRIPT_UTILITY_FNV1A_HPP_ - -#include #include "../chaiscript_defines.hpp" +#include - -namespace chaiscript -{ - - - namespace utility - { - - +namespace chaiscript { + namespace utility { static constexpr std::uint32_t fnv1a_32(const char *s, std::uint32_t h = 0x811c9dc5) { #ifdef __GNUC__ #pragma GCC diagnostic push @@ -30,7 +22,7 @@ namespace chaiscript #pragma warning(push) #pragma warning(disable : 4307) #endif - return (*s == 0) ? h : fnv1a_32(s+1, ((h ^ (*s)) * 0x01000193)); + return (*s == 0) ? h : fnv1a_32(s + 1, ((h ^ (*s)) * 0x01000193)); #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif @@ -38,13 +30,9 @@ namespace chaiscript #ifdef __GNUC__ #pragma GCC diagnostic pop #endif - } - - } - - -} + } // namespace utility +} // namespace chaiscript #endif diff --git a/include/chaiscript/utility/hash.hpp b/include/chaiscript/utility/hash.hpp new file mode 100644 index 00000000..15bc4d9c --- /dev/null +++ b/include/chaiscript/utility/hash.hpp @@ -0,0 +1,94 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +#ifndef CHAISCRIPT_UTILITY_FNV1A_HPP_ +#define CHAISCRIPT_UTILITY_FNV1A_HPP_ + +#include "../chaiscript_defines.hpp" +#include + +namespace chaiscript { + namespace utility { + namespace fnv1a { + template + static constexpr std::uint32_t hash(Itr begin, Itr end) noexcept { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4307) +#endif + std::uint32_t h = 0x811c9dc5; + + while (begin != end) { + h = (h ^ (*begin)) * 0x01000193; + ++begin; + } + return h; + +#ifdef CHAISCRIPT_MSVC +#pragma warning(pop) +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + template + static constexpr std::uint32_t hash(const char (&str)[N]) noexcept { + return hash(std::begin(str), std::end(str) - 1); + } + + static constexpr std::uint32_t hash(std::string_view sv) noexcept { + return hash(sv.begin(), sv.end()); + } + + static inline std::uint32_t hash(const std::string &s) noexcept { + return hash(s.begin(), s.end()); + } + } // namespace fnv1a + + namespace jenkins_one_at_a_time { + template + static constexpr std::uint32_t hash(Itr begin, Itr end) noexcept { + std::uint32_t hash = 0; + + while (begin != end) { + hash += std::uint32_t(*begin); + hash += hash << 10; + hash ^= hash >> 6; + ++begin; + } + + hash += hash << 3; + hash ^= hash >> 11; + hash += hash << 15; + return hash; + } + + template + static constexpr std::uint32_t hash(const char (&str)[N]) noexcept { + return hash(std::begin(str), std::end(str) - 1); + } + + static constexpr std::uint32_t hash(std::string_view sv) noexcept { + return hash(sv.begin(), sv.end()); + } + + static inline std::uint32_t hash(const std::string &s) noexcept { + return hash(s.begin(), s.end()); + } + } // namespace jenkins_one_at_a_time + + using fnv1a::hash; + } // namespace utility +} // namespace chaiscript + +#endif diff --git a/include/chaiscript/utility/json.hpp b/include/chaiscript/utility/json.hpp index c6988d54..6bc57613 100644 --- a/include/chaiscript/utility/json.hpp +++ b/include/chaiscript/utility/json.hpp @@ -1,43 +1,36 @@ -// From github.com/nbsdx/SimpleJSON. +// From github.com/nbsdx/SimpleJSON. // Released under the DWTFYW PL // - -#pragma once - #ifndef SIMPLEJSON_HPP #define SIMPLEJSON_HPP - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "../chaiscript_defines.hpp" +#include "quick_flat_map.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace json { + using std::enable_if; + using std::initializer_list; + using std::is_convertible; + using std::is_floating_point; + using std::is_integral; + using std::is_same; -using std::enable_if; -using std::initializer_list; -using std::is_same; -using std::is_convertible; -using std::is_integral; -using std::is_floating_point; - - - - -class JSON -{ + class JSON { public: enum class Class { - Null, + Null = 0, Object, Array, String, @@ -47,629 +40,559 @@ class JSON }; private: - - struct QuickFlatMap - { - auto find(const std::string &s) { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); - } - - auto find(const std::string &s) const { - return std::find_if(std::begin(data), std::end(data), [&s](const auto &d) { return d.first == s; }); - } - - auto size() const { - return data.size(); - } - - auto begin() const { - return data.begin(); - } - - auto end() const { - return data.end(); - } - - - auto begin() { - return data.begin(); - } - - auto end() { - return data.end(); - } - - - JSON &operator[](const std::string &s) { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; - } else { - data.emplace_back(s, JSON()); - return data.back().second; - } - } - - JSON &at(const std::string &s) { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; - } else { - throw std::out_of_range("Unknown key: " + s); - } - } - - const JSON &at(const std::string &s) const { - const auto itr = find(s); - if (itr != data.end()) { - return itr->second; - } else { - throw std::out_of_range("Unknown key: " + s); - } - } - - size_t count(const std::string &s) const { - return (find(s) != data.end())?1:0; - } - - std::vector> data; - - using iterator = decltype(data)::iterator; - using const_iterator = decltype(data)::const_iterator; - - - }; + using Data + = 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) + : d(make_type(c)) { + } template - auto clone(const std::unique_ptr &ptr) { - if (ptr != nullptr) { - return std::make_unique(*ptr); - } else { - return std::unique_ptr(nullptr); - } + Internal(T t) + : d(std::move(t)) { + } + + static Data make_type(Class c) { + switch (c) { + case Class::Null: + return nullptr; + case Class::Object: + return chaiscript::utility::QuickFlatMap{}; + case Class::Array: + return std::vector{}; + case Class::String: + return std::string{}; + case Class::Floating: + return double{}; + case Class::Integral: + return std::int64_t{}; + case Class::Boolean: + return bool{}; } - - Internal( double d ) : Float( d ), Type(Class::Floating) {} - Internal( int64_t l ) : Int( l ), Type(Class::Integral) {} - Internal( bool b ) : Bool( b ), Type(Class::Boolean) {} - Internal( std::string s ) : String(std::make_unique(std::move(s))), Type(Class::String) {} - Internal() : Type(Class::Null) {} - - Internal(Class t_type) { - set_type(t_type); + throw std::runtime_error("unknown type"); } - Internal(const Internal &other) - : List(clone(other.List)), - Map(clone(other.Map)), - String(clone(other.String)), - Float(other.Float), - Int(other.Int), - Bool(other.Bool), - Type(other.Type) - { - } - - Internal &operator=(const Internal &other) - { - List = clone(other.List); - Map = clone(other.Map); - String = clone(other.String); - Float = other.Float; - Int = other.Int; - Bool = other.Bool; - Type = other.Type; - return *this; - } - - void set_type( Class type ) { - if( type == Type ) { - return; + void set_type(Class c) { + if (type() != c) { + d = make_type(c); } - - Map.reset(); - List.reset(); - String.reset(); - - switch( type ) { - case Class::Object: Map = std::make_unique(); break; - case Class::Array: List = std::make_unique>(); break; - case Class::String: String = std::make_unique(); break; - case Class::Floating: Float = 0.0; break; - case Class::Integral: Int = 0; break; - case Class::Boolean: Bool = false; break; - case Class::Null: break; - } - - Type = type; } - Internal(Internal &&) = default; - Internal &operator=(Internal &&) = default; + Class type() const noexcept { return Class(d.index()); } - std::unique_ptr> List; - std::unique_ptr Map; - std::unique_ptr String; - double Float = 0; - int64_t Int = 0; - bool Bool = false; + template + decltype(auto) visit_or(Visitor &&visitor, Or &&other) const { + if (type() == Class(ClassValue)) { + return visitor(std::get(ClassValue)>(d)); + } else { + return other(); + } + } - Class Type = Class::Null; + template + auto &get_set_type() { + set_type(ClassValue); + return (std::get(ClassValue)>(d)); + } + + auto &Map() { return get_set_type(); } + auto &Vector() { return get_set_type(); } + auto &String() { return get_set_type(); } + auto &Int() { return get_set_type(); } + 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); } + + Data d; }; Internal internal; public: + template + class JSONWrapper { + Container *object = nullptr; - template - class JSONWrapper { - Container *object = nullptr; + public: + JSONWrapper(Container *val) + : object(val) { + } + JSONWrapper(std::nullptr_t) {} - public: - JSONWrapper( Container *val ) : object( val ) {} - 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(); } + }; - 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(); } - }; + template + class JSONConstWrapper { + const Container *object = nullptr; - template - class JSONConstWrapper { - const Container *object = nullptr; + public: + JSONConstWrapper(const Container *val) + : object(val) { + } + JSONConstWrapper(std::nullptr_t) {} - public: - JSONConstWrapper( const Container *val ) : object( val ) {} - JSONConstWrapper( std::nullptr_t ) {} - - typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); } - typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); } - }; + typename 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(); } + }; JSON() = default; - JSON( std::nullptr_t ) {} + JSON(std::nullptr_t) {} explicit JSON(Class type) - : internal(type) - { + : internal(type) { } - JSON( initializer_list list ) - : internal(Class::Object) - { - for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i ) { - operator[]( i->to_string() ) = *std::next( i ); + JSON(initializer_list list) + : internal(Class::Object) { + for (auto i = list.begin(), e = list.end(); i != e; ++i, ++i) { + operator[](i->to_string()) = *std::next(i); } } - template - explicit JSON( T b, typename enable_if::value>::type* = nullptr ) : internal( static_cast(b) ) {} - - template - explicit JSON( T i, typename enable_if::value && !is_same::value>::type* = nullptr ) : internal( static_cast(i) ) {} - - template - explicit JSON( T f, typename enable_if::value>::type* = nullptr ) : internal( static_cast(f) ) {} - - template - explicit JSON( T s, typename enable_if::value>::type* = nullptr ) : internal( static_cast(s) ) {} - - - - static JSON Load( const std::string & ); - - JSON& operator[]( const std::string &key ) { - internal.set_type( Class::Object ); - return internal.Map->operator[]( key ); + template + explicit JSON(T b, typename enable_if::value>::type * = nullptr) noexcept + : internal(static_cast(b)) { } - JSON& operator[]( const size_t index ) { - internal.set_type( Class::Array ); - if( index >= internal.List->size() ) { - internal.List->resize( index + 1 ); + template + explicit JSON(T i, typename enable_if::value && !is_same::value>::type * = nullptr) noexcept + : internal(static_cast(i)) { + } + + template + explicit JSON(T f, typename enable_if::value>::type * = nullptr) noexcept + : internal(static_cast(f)) { + } + + template + explicit JSON(T s, typename enable_if::value>::type * = nullptr) + : internal(static_cast(s)) { + } + + static JSON Load(const std::string &); + + JSON &operator[](const std::string &key) { return internal.Map().operator[](key); } + + JSON &operator[](const size_t index) { + auto &vec = internal.Vector(); + if (index >= vec.size()) { + vec.resize(index + 1); } - return internal.List->operator[]( index ); + return vec.operator[](index); } + JSON &at(const std::string &key) { return operator[](key); } - JSON &at( const std::string &key ) { - return operator[]( key ); + const JSON &at(const std::string &key) const { + return internal.visit_or([&](const auto &m) -> const JSON & { return m.at(key); }, + []() -> const JSON & { throw std::range_error("Not an object, no keys"); }); } - const JSON &at( const std::string &key ) const { - return internal.Map->at( key ); + JSON &at(size_t index) { return operator[](index); } + + const JSON &at(size_t index) const { + return internal.visit_or([&](const auto &m) -> const JSON & { return m.at(index); }, + []() -> const JSON & { throw std::range_error("Not an array, no indexes"); }); } - JSON &at( size_t index ) { - return operator[]( index ); + auto length() const noexcept { + return internal.visit_or([&](const auto &m) { return static_cast(m.size()); }, []() { return -1; }); } - const JSON &at( size_t index ) const { - return internal.List->at( index ); + bool has_key(const std::string &key) const noexcept { + return internal.visit_or([&](const auto &m) { return m.count(key) != 0; }, []() { return false; }); } - - long length() const { - if( internal.Type == Class::Array ) { - return static_cast(internal.List->size()); + int size() const noexcept { + if (auto m = internal.Map(); m != nullptr) { + return static_cast(m->size()); + } + if (auto v = internal.Vector(); v != nullptr) { + return static_cast(v->size()); } else { return -1; } } - bool has_key( const std::string &key ) const { - if( internal.Type == Class::Object ) { - return internal.Map->count(key) != 0; - } - - return false; - } - - int size() const { - if( internal.Type == Class::Object ) { - return static_cast(internal.Map->size()); - } else if( internal.Type == Class::Array ) { - return static_cast(internal.List->size()); - } else { - return -1; - } - } - - Class JSONType() const { return internal.Type; } + Class JSONType() const noexcept { return internal.type(); } /// Functions for getting primitives from the JSON object. - bool is_null() const { return internal.Type == Class::Null; } + bool is_null() const noexcept { return internal.type() == Class::Null; } - std::string to_string() const { bool b; return to_string( b ); } - std::string to_string( bool &ok ) const { - ok = (internal.Type == Class::String); - return ok ? *internal.String : std::string(""); + std::string to_string() const noexcept { + return internal.visit_or([](const auto &o) { return o; }, []() { return std::string{}; }); + } + double to_float() const noexcept { + return internal.visit_or([](const auto &o) { return o; }, []() { return double{}; }); + } + std::int64_t to_int() const noexcept { + return internal.visit_or([](const auto &o) { return o; }, []() { return std::int64_t{}; }); + } + bool to_bool() const noexcept { + return internal.visit_or([](const auto &o) { return o; }, []() { return false; }); } - double to_float() const { bool b; return to_float( b ); } - double to_float( bool &ok ) const { - ok = (internal.Type == Class::Floating); - return ok ? internal.Float : 0.0; + JSONWrapper> object_range() { + return std::get_if(Class::Object)>(&internal.d); } - int64_t to_int() const { bool b; return to_int( b ); } - int64_t to_int( bool &ok ) const { - ok = (internal.Type == Class::Integral); - return ok ? internal.Int : 0; + JSONWrapper> array_range() { return std::get_if(Class::Array)>(&internal.d); } + + JSONConstWrapper> object_range() const { + return std::get_if(Class::Object)>(&internal.d); } - bool to_bool() const { bool b; return to_bool( b ); } - bool to_bool( bool &ok ) const { - ok = (internal.Type == Class::Boolean); - return ok ? internal.Bool : false; - } + JSONConstWrapper> array_range() const { return std::get_if(Class::Array)>(&internal.d); } - JSONWrapper object_range() { - if( internal.Type == Class::Object ) { - return JSONWrapper( internal.Map.get() ); - } else { - return JSONWrapper( nullptr ); - } - } - - JSONWrapper> array_range() { - if( internal.Type == Class::Array ) { - return JSONWrapper>( internal.List.get() ); - } else { - return JSONWrapper>( nullptr ); - } - } - - JSONConstWrapper object_range() const { - if( internal.Type == Class::Object ) { - return JSONConstWrapper( internal.Map.get() ); - } else { - return JSONConstWrapper( nullptr ); - } - } - - - JSONConstWrapper> array_range() const { - if( internal.Type == Class::Array ) { - return JSONConstWrapper>( internal.List.get() ); - } else { - return JSONConstWrapper>( nullptr ); - } - } - - std::string dump( long depth = 1, std::string tab = " ") const { - switch( internal.Type ) { + std::string dump(long depth = 1, std::string tab = " ") const { + switch (internal.type()) { case Class::Null: return "null"; case Class::Object: { - std::string pad = ""; - for( long i = 0; i < depth; ++i, pad += tab ) { } + std::string pad = ""; + for (long i = 0; i < depth; ++i, pad += tab) { + } - std::string s = "{\n"; - bool skip = true; - for( auto &p : *internal.Map ) { - if( !skip ) { s += ",\n"; } - s += ( pad + "\"" + json_escape(p.first) + "\" : " + p.second.dump( depth + 1, tab ) ); - skip = false; - } - s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ; - return s; - } + std::string s = "{\n"; + bool skip = true; + for (auto &p : *internal.Map()) { + if (!skip) { + s += ",\n"; + } + s += (pad + "\"" + json_escape(p.first) + "\" : " + p.second.dump(depth + 1, tab)); + skip = false; + } + s += ("\n" + pad.erase(0, 2) + "}"); + return s; + } case Class::Array: { - std::string s = "["; - bool skip = true; - for( auto &p : *internal.List ) { - if( !skip ) { s += ", "; } - s += p.dump( depth + 1, tab ); - skip = false; - } - s += "]"; - return s; - } + std::string s = "["; + bool skip = true; + for (auto &p : *internal.Vector()) { + if (!skip) { + s += ", "; + } + s += p.dump(depth + 1, tab); + skip = false; + } + s += "]"; + return s; + } case Class::String: - return "\"" + json_escape( *internal.String ) + "\""; + return "\"" + json_escape(*internal.String()) + "\""; case Class::Floating: - return std::to_string( internal.Float ); + return std::to_string(*internal.Float()); case Class::Integral: - return std::to_string( internal.Int ); + return std::to_string(*internal.Int()); case Class::Boolean: - return internal.Bool ? "true" : "false"; + return *internal.Bool() ? "true" : "false"; } throw std::runtime_error("Unhandled JSON type"); } - private: - static std::string json_escape( const std::string &str ) { + static std::string json_escape(const std::string &str) { std::string output; - for(char i : str) { - switch( i ) { - case '\"': output += "\\\""; break; - case '\\': output += "\\\\"; break; - case '\b': output += "\\b"; break; - case '\f': output += "\\f"; break; - case '\n': output += "\\n"; break; - case '\r': output += "\\r"; break; - case '\t': output += "\\t"; break; - default : output += i; break; + for (char i : str) { + switch (i) { + case '\"': + output += "\\\""; + break; + case '\\': + output += "\\\\"; + break; + case '\b': + output += "\\b"; + break; + case '\f': + output += "\\f"; + break; + case '\n': + output += "\\n"; + break; + case '\r': + output += "\\r"; + break; + case '\t': + output += "\\t"; + break; + default: + output += i; + break; } -} + } return output; } - private: -}; + }; - -struct JSONParser { - static bool isspace(const char c) - { + struct JSONParser { + static bool isspace(const char c) noexcept { #ifdef CHAISCRIPT_MSVC - // MSVC warns on these line in some circumstances +// MSVC warns on these line in some circumstances #pragma warning(push) #pragma warning(disable : 6330) #endif - return ::isspace(c) != 0; + return ::isspace(c) != 0; #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif - - - } - - static void consume_ws( const std::string &str, size_t &offset ) { - while( isspace( str.at(offset) ) && offset <= str.size() ) { ++offset; } - } - - static JSON parse_object( const std::string &str, size_t &offset ) { - JSON Object( JSON::Class::Object ); - - ++offset; - consume_ws( str, offset ); - if( str.at(offset) == '}' ) { - ++offset; return Object; } - for (;offset= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') ) { - val += c; - } else { - throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); - } - } - offset += 4; - } break; - default : val += '\\'; break; - } - } else { - val += c; - } - } - ++offset; - return JSON(val); - } - - static JSON parse_number( const std::string &str, size_t &offset ) { - std::string val, exp_str; - char c = '\0'; - bool isDouble = false; - bool isNegative = false; - int64_t exp = 0; - bool isExpNegative = false; - if( offset < str.size() && str.at(offset) == '-' ) { - isNegative = true; ++offset; - } - for (; offset < str.size() ;) { - c = str.at(offset++); - if( c >= '0' && c <= '9' ) { - val += c; - } else if( c == '.' && !isDouble ) { - val += c; - isDouble = true; - } else { - break; - } - } - if( offset < str.size() && (c == 'E' || c == 'e' )) { - c = str.at(offset++); - if( c == '-' ) { - isExpNegative = true; - } else if( c == '+' ) { - // do nothing - } else { - --offset; + consume_ws(str, offset); + if (str.at(offset) == '}') { + ++offset; + return Object; } - for (; offset < str.size() ;) { - c = str.at(offset++); - if( c >= '0' && c <= '9' ) { - exp_str += c; - } else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) { - throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'"); + for (; offset < str.size();) { + JSON Key = parse_next(str, offset); + consume_ws(str, offset); + if (str.at(offset) != ':') { + throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str.at(offset) + "'\n"); } - else { + consume_ws(str, ++offset); + JSON Value = parse_next(str, offset); + Object[Key.to_string()] = Value; + + consume_ws(str, offset); + if (str.at(offset) == ',') { + ++offset; + continue; + } else if (str.at(offset) == '}') { + ++offset; + break; + } else { + throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str.at(offset) + "'\n"); + } + } + + return Object; + } + + static JSON parse_array(const std::string &str, size_t &offset) { + JSON Array(JSON::Class::Array); + size_t index = 0; + + ++offset; + consume_ws(str, offset); + if (str.at(offset) == ']') { + ++offset; + return Array; + } + + for (; offset < str.size();) { + Array[index++] = parse_next(str, offset); + consume_ws(str, offset); + + if (str.at(offset) == ',') { + ++offset; + continue; + } else if (str.at(offset) == ']') { + ++offset; + break; + } else { + throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str.at(offset) + "'\n"); + } + } + + return Array; + } + + static JSON parse_string(const std::string &str, size_t &offset) { + std::string val; + for (char c = str.at(++offset); c != '\"'; c = str.at(++offset)) { + if (c == '\\') { + switch (str.at(++offset)) { + case '\"': + val += '\"'; + break; + case '\\': + val += '\\'; + break; + case '/': + val += '/'; + break; + case 'b': + val += '\b'; + break; + case 'f': + val += '\f'; + break; + case 'n': + val += '\n'; + break; + case 'r': + val += '\r'; + break; + case 't': + val += '\t'; + break; + case 'u': { + val += "\\u"; + for (size_t i = 1; i <= 4; ++i) { + c = str.at(offset + i); + if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { + val += c; + } else { + throw std::runtime_error( + std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'"); + } + } + offset += 4; + } break; + default: + val += '\\'; + break; + } + } else { + val += c; + } + } + ++offset; + return JSON(val); + } + + static JSON parse_number(const std::string &str, size_t &offset) { + std::string val, exp_str; + char c = '\0'; + bool isDouble = false; + bool isNegative = false; + std::int64_t exp = 0; + bool isExpNegative = false; + if (offset < str.size() && str.at(offset) == '-') { + isNegative = true; + ++offset; + } + for (; offset < str.size();) { + c = str.at(offset++); + if (c >= '0' && c <= '9') { + val += c; + } else if (c == '.' && !isDouble) { + val += c; + isDouble = true; + } else { break; } } - exp = chaiscript::parse_num( exp_str ) * (isExpNegative?-1:1); - } - else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) { - throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); - } - --offset; + if (offset < str.size() && (c == 'E' || c == 'e')) { + c = str.at(offset++); + if (c == '-') { + isExpNegative = true; + } else if (c == '+') { + // do nothing + } else { + --offset; + } - if( isDouble ) { - return JSON((isNegative?-1:1) * chaiscript::parse_num( val ) * std::pow( 10, exp )); - } else { - if( !exp_str.empty() ) { - return JSON((isNegative?-1:1) * static_cast(chaiscript::parse_num( val )) * std::pow( 10, exp )); + for (; offset < str.size();) { + c = str.at(offset++); + if (c >= '0' && c <= '9') { + exp_str += c; + } else if (!isspace(c) && c != ',' && c != ']' && c != '}') { + throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'"); + } else { + break; + } + } + exp = chaiscript::parse_num(exp_str) * (isExpNegative ? -1 : 1); + } else if (offset < str.size() && (!isspace(c) && c != ',' && c != ']' && c != '}')) { + throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'"); + } + --offset; + + if (isDouble) { + return JSON((isNegative ? -1 : 1) * chaiscript::parse_num(val) * std::pow(10, exp)); } else { - return JSON((isNegative?-1:1) * chaiscript::parse_num( val )); + if (!exp_str.empty()) { + return JSON((isNegative ? -1 : 1) * static_cast(chaiscript::parse_num(val)) * std::pow(10, exp)); + } else { + return JSON((isNegative ? -1 : 1) * chaiscript::parse_num(val)); + } } } - } - static JSON parse_bool( const std::string &str, size_t &offset ) { - if( str.substr( offset, 4 ) == "true" ) { + static JSON parse_bool(const std::string &str, size_t &offset) { + if (str.substr(offset, 4) == "true") { + offset += 4; + return JSON(true); + } else if (str.substr(offset, 5) == "false") { + offset += 5; + return JSON(false); + } else { + throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr(offset, 5) + "'"); + } + } + + static JSON parse_null(const std::string &str, size_t &offset) { + if (str.substr(offset, 4) != "null") { + throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr(offset, 4) + "'"); + } offset += 4; - return JSON(true); - } else if( str.substr( offset, 5 ) == "false" ) { - offset += 5; - return JSON(false); - } else { - throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'"); + return JSON(); } - } - static JSON parse_null( const std::string &str, size_t &offset ) { - if( str.substr( offset, 4 ) != "null" ) { - throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'"); + static JSON parse_next(const std::string &str, size_t &offset) { + char value; + consume_ws(str, offset); + value = str.at(offset); + switch (value) { + case '[': + return parse_array(str, offset); + case '{': + return parse_object(str, offset); + case '\"': + return parse_string(str, offset); + case 't': + case 'f': + return parse_bool(str, offset); + case 'n': + return parse_null(str, offset); + default: + if ((value <= '9' && value >= '0') || value == '-') { + return parse_number(str, offset); + } + } + throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'"); } - offset += 4; - return JSON(); + }; + + inline JSON JSON::Load(const std::string &str) { + size_t offset = 0; + return JSONParser::parse_next(str, offset); } - static JSON parse_next( const std::string &str, size_t &offset ) { - char value; - consume_ws( str, offset ); - value = str.at(offset); - switch( value ) { - case '[' : return parse_array( str, offset ); - case '{' : return parse_object( str, offset ); - case '\"': return parse_string( str, offset ); - case 't' : - case 'f' : return parse_bool( str, offset ); - case 'n' : return parse_null( str, offset ); - default : if( ( value <= '9' && value >= '0' ) || value == '-' ) { - return parse_number( str, offset ); - } - } - throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'"); - } - -}; - -inline JSON JSON::Load( const std::string &str ) { - size_t offset = 0; - return JSONParser::parse_next( str, offset ); -} - } // End Namespace json - -#endif +#endif diff --git a/include/chaiscript/utility/json_wrap.hpp b/include/chaiscript/utility/json_wrap.hpp index 656bf21d..d478f407 100644 --- a/include/chaiscript/utility/json_wrap.hpp +++ b/include/chaiscript/utility/json_wrap.hpp @@ -3,154 +3,129 @@ #include "json.hpp" -namespace chaiscript -{ - class json_wrap - { - public: +namespace chaiscript { + class json_wrap { + public: + static Module &library(Module &m) { + m.add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json"); + m.add(chaiscript::fun(&json_wrap::to_json), "to_json"); - static Module& library(Module& m) - { + return m; + } - m.add(chaiscript::fun([](const std::string &t_str) { return from_json(t_str); }), "from_json"); - m.add(chaiscript::fun(&json_wrap::to_json), "to_json"); + private: + static Boxed_Value from_json(const json::JSON &t_json) { + switch (t_json.JSONType()) { + case json::JSON::Class::Null: + return Boxed_Value(); + case json::JSON::Class::Object: { + std::map m; - return m; - - } - - private: - - static Boxed_Value from_json(const json::JSON &t_json) - { - switch( t_json.JSONType() ) { - case json::JSON::Class::Null: - return Boxed_Value(); - case json::JSON::Class::Object: - { - std::map m; - - for (const auto &p : t_json.object_range()) - { - m.insert(std::make_pair(p.first, from_json(p.second))); - } - - return Boxed_Value(m); - } - case json::JSON::Class::Array: - { - std::vector vec; - - for (const auto &p : t_json.array_range()) - { - vec.emplace_back(from_json(p)); - } - - return Boxed_Value(vec); - } - case json::JSON::Class::String: - return Boxed_Value(t_json.to_string()); - case json::JSON::Class::Floating: - return Boxed_Value(t_json.to_float()); - case json::JSON::Class::Integral: - return Boxed_Value(t_json.to_int()); - case json::JSON::Class::Boolean: - return Boxed_Value(t_json.to_bool()); - } - - throw std::runtime_error("Unknown JSON type"); - } - - static Boxed_Value from_json(const std::string &t_json) - { - try { - return from_json( json::JSON::Load(t_json) ); - } catch (const std::out_of_range& ) { - throw std::runtime_error("Unparsed JSON input"); - } - } - - static std::string to_json(const Boxed_Value &t_bv) - { - return to_json_object(t_bv).dump(); - } - - static json::JSON to_json_object(const Boxed_Value &t_bv) - { - try { - const std::map m = chaiscript::boxed_cast &>(t_bv); - - json::JSON obj(json::JSON::Class::Object); - for (const auto &o : m) - { - obj[o.first] = to_json_object(o.second); + for (const auto &p : t_json.object_range()) { + m.insert(std::make_pair(p.first, from_json(p.second))); } - return obj; - } catch (const chaiscript::exception::bad_boxed_cast &) { - // not a map + + return Boxed_Value(m); } + case json::JSON::Class::Array: { + std::vector vec; - try { - const std::vector v = chaiscript::boxed_cast &>(t_bv); - - json::JSON obj(json::JSON::Class::Array); - for (size_t i = 0; i < v.size(); ++i) - { - obj[i] = to_json_object(v[i]); + for (const auto &p : t_json.array_range()) { + vec.emplace_back(from_json(p)); } - return obj; - } catch (const chaiscript::exception::bad_boxed_cast &) { - // not a vector + + return Boxed_Value(vec); } - - - try { - Boxed_Number bn(t_bv); - if (Boxed_Number::is_floating_point(t_bv)) - { - return json::JSON(bn.get_as()); - } else { - return json::JSON(bn.get_as()); - } - } catch (const chaiscript::detail::exception::bad_any_cast &) { - // not a number - } - - try { - return json::JSON(boxed_cast(t_bv)); - } catch (const chaiscript::exception::bad_boxed_cast &) { - // not a bool - } - - try { - return json::JSON(boxed_cast(t_bv)); - } catch (const chaiscript::exception::bad_boxed_cast &) { - // not a string - } - - - try { - const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); - - json::JSON obj(json::JSON::Class::Object); - for (const auto &attr : o.get_attrs()) - { - obj[attr.first] = to_json_object(attr.second); - } - return obj; - } catch (const chaiscript::exception::bad_boxed_cast &) { - // not a dynamic object - } - - if (t_bv.is_null()) return json::JSON(); // a null value - - throw std::runtime_error("Unknown object type to convert to JSON"); + case json::JSON::Class::String: + return Boxed_Value(t_json.to_string()); + case json::JSON::Class::Floating: + return Boxed_Value(t_json.to_float()); + case json::JSON::Class::Integral: + return Boxed_Value(t_json.to_int()); + case json::JSON::Class::Boolean: + return Boxed_Value(t_json.to_bool()); } + throw std::runtime_error("Unknown JSON type"); + } + static Boxed_Value from_json(const std::string &t_json) { + try { + return from_json(json::JSON::Load(t_json)); + } catch (const std::out_of_range &) { + throw std::runtime_error("Unparsed JSON input"); + } + } + + static std::string to_json(const Boxed_Value &t_bv) { return to_json_object(t_bv).dump(); } + + static json::JSON to_json_object(const Boxed_Value &t_bv) { + try { + const std::map m = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj(json::JSON::Class::Object); + for (const auto &o : m) { + obj[o.first] = to_json_object(o.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a map + } + + try { + const std::vector v = chaiscript::boxed_cast &>(t_bv); + + json::JSON obj(json::JSON::Class::Array); + for (size_t i = 0; i < v.size(); ++i) { + obj[i] = to_json_object(v[i]); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a vector + } + + try { + Boxed_Number bn(t_bv); + if (Boxed_Number::is_floating_point(t_bv)) { + return json::JSON(bn.get_as()); + } else { + return json::JSON(bn.get_as()); + } + } catch (const chaiscript::detail::exception::bad_any_cast &) { + // not a number + } + + try { + return json::JSON(boxed_cast(t_bv)); + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a bool + } + + try { + return json::JSON(boxed_cast(t_bv)); + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a string + } + + try { + const chaiscript::dispatch::Dynamic_Object &o = boxed_cast(t_bv); + + json::JSON obj(json::JSON::Class::Object); + for (const auto &attr : o.get_attrs()) { + obj[attr.first] = to_json_object(attr.second); + } + return obj; + } catch (const chaiscript::exception::bad_boxed_cast &) { + // not a dynamic object + } + + if (t_bv.is_null()) + return json::JSON(); // a null value + + throw std::runtime_error("Unknown object type to convert to JSON"); + } }; - -} +} // namespace chaiscript #endif diff --git a/include/chaiscript/utility/quick_flat_map.hpp b/include/chaiscript/utility/quick_flat_map.hpp new file mode 100644 index 00000000..bf189a8f --- /dev/null +++ b/include/chaiscript/utility/quick_flat_map.hpp @@ -0,0 +1,133 @@ +#ifndef CHAISCRIPT_UTILITY_QUICK_FLAT_MAP_HPP +#define CHAISCRIPT_UTILITY_QUICK_FLAT_MAP_HPP + +namespace chaiscript::utility { + template> + struct QuickFlatMap { + Comparator comparator; + + template + auto find(const Lookup &s) noexcept { + return std::find_if(std::begin(data), std::end(data), [&s, this](const auto &d) { return comparator(d.first, s); }); + } + + template + auto find(const Lookup &s) const noexcept { + return std::find_if(std::cbegin(data), std::cend(data), [&s, this](const auto &d) { return comparator(d.first, s); }); + } + + template + auto find(const Lookup &s, const std::size_t t_hint) const noexcept { + if (data.size() > t_hint && comparator(data[t_hint].first, s)) { + const auto begin = std::cbegin(data); + return std::next(begin, static_cast>::difference_type>(t_hint)); + } else { + return find(s); + } + } + + auto size() const noexcept { return data.size(); } + + auto begin() const noexcept { return data.begin(); } + + auto end() const noexcept { return data.end(); } + + auto begin() noexcept { return data.begin(); } + + auto end() noexcept { return data.end(); } + + auto &back() noexcept { return data.back(); } + + const auto &back() const noexcept { return data.back(); } + + Value &operator[](const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + grow(); + return data.emplace_back(s, Value()).second; + } + } + + Value &at_index(const std::size_t idx) noexcept { return data[idx].second; } + + const Value &at_index(const std::size_t idx) const noexcept { return data[idx].second; } + + bool empty() const noexcept { return data.empty(); } + + template + void assign(Itr begin, Itr end) { + data.assign(begin, end); + } + + Value &at(const Key &s) { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + template + auto insert_or_assign(Key &&key, M &&m) { + if (auto itr = find(key); itr != data.end()) { + *itr = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.emplace(data.end(), std::move(key), std::forward(m)), true}; + } + } + + template + auto insert_or_assign(const Key &key, M &&m) { + if (auto itr = find(key); itr != data.end()) { + itr->second = std::forward(m); + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.emplace(data.end(), key, std::forward(m)), true}; + } + } + + const Value &at(const Key &s) const { + const auto itr = find(s); + if (itr != data.end()) { + return itr->second; + } else { + throw std::out_of_range("Unknown key: " + s); + } + } + + template + size_t count(const Lookup &s) const noexcept { + return (find(s) != data.end()) ? 1 : 0; + } + + std::vector> data; + + using value_type = std::pair; + using iterator = typename decltype(data)::iterator; + using const_iterator = typename decltype(data)::const_iterator; + + std::pair insert(value_type &&value) { + if (const auto itr = find(value.first); itr != data.end()) { + return std::pair{itr, false}; + } else { + grow(); + return std::pair{data.insert(data.end(), std::move(value)), true}; + } + } + + void grow() { + if ((data.capacity() - data.size()) == 0) { + data.reserve(data.size() + 2); + } + } + }; + +} // namespace chaiscript::utility + +#endif diff --git a/include/chaiscript/utility/stack_vector.hpp b/include/chaiscript/utility/stack_vector.hpp new file mode 100644 index 00000000..3b1e11ab --- /dev/null +++ b/include/chaiscript/utility/stack_vector.hpp @@ -0,0 +1,49 @@ +// This file is distributed under the BSD License. +// See "license.txt" for details. +// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) +// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// http://www.chaiscript.com + +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +#ifndef CHAISCRIPT_STACK_VECTOR_HPP_ +#define CHAISCRIPT_STACK_VECTOR_HPP_ + +#include +#include +#include + +template +struct Stack_Vector { + constexpr static auto aligned_size = sizeof(T) + (sizeof(T) & std::alignment_of_v) > 0 ? std::alignment_of_v : 0; + + alignas(std::alignment_of_v) char data[aligned_size * MaxSize]; + + [[nodiscard]] T &operator[](const std::size_t idx) noexcept { return *reinterpret_cast(&data + aligned_size * idx); } + + [[nodiscard]] const T &operator[](const std::size_t idx) const noexcept { + return *reinterpret_cast(&data + aligned_size * idx); + } + + template + T &emplace_back(Param &&...param) { + auto *p = new (&(*this)[m_size++]) T(std::forward(param)...); + return *p; + }; + + auto size() const noexcept { return m_size; }; + + void pop_back() noexcept(std::is_nothrow_destructible_v) { (*this)[--m_size].~T(); } + + ~Stack_Vector() noexcept(std::is_nothrow_destructible_v) { + auto loc = m_size - 1; + for (std::size_t pos = 0; pos < m_size; ++pos) { + (*this)[loc--].~T(); + } + } + + std::size_t m_size{0}; +}; + +#endif CHAISCRIPT_STACK_VECTOR_HPP_ diff --git a/include/chaiscript/utility/static_string.hpp b/include/chaiscript/utility/static_string.hpp index 49edfbd8..8613fd31 100644 --- a/include/chaiscript/utility/static_string.hpp +++ b/include/chaiscript/utility/static_string.hpp @@ -1,37 +1,54 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com #ifndef CHAISCRIPT_UTILITY_STATIC_STRING_HPP_ #define CHAISCRIPT_UTILITY_STATIC_STRING_HPP_ -namespace chaiscript -{ - namespace utility - { +namespace chaiscript::utility { + struct Static_String { + template + constexpr Static_String(const char (&str)[N]) noexcept + : m_size(N - 1) + , data(&str[0]) { + } - struct Static_String - { - template - constexpr Static_String(const char (&str)[N]) - : m_size(N-1), data(&str[0]) - { + constexpr size_t size() const noexcept { return m_size; } + + constexpr const char *c_str() const noexcept { return data; } + + constexpr auto begin() const noexcept { return data; } + + constexpr auto end() const noexcept { return data + 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; } - - constexpr size_t size() const { - return m_size; + ++b1; + ++b2; } + return true; + } - constexpr const char *c_str() const { - return data; - } + 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; - }; - } -} + const size_t m_size; + const char *data = nullptr; + }; +} // namespace chaiscript::utility #endif diff --git a/include/chaiscript/utility/utility.hpp b/include/chaiscript/utility/utility.hpp index b743c451..22a1ff01 100644 --- a/include/chaiscript/utility/utility.hpp +++ b/include/chaiscript/utility/utility.hpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #ifndef CHAISCRIPT_UTILITY_UTILITY_HPP_ #define CHAISCRIPT_UTILITY_UTILITY_HPP_ @@ -16,108 +15,90 @@ #include #include -#include "../language/chaiscript_common.hpp" -#include "../dispatchkit/register_function.hpp" #include "../dispatchkit/operators.hpp" +#include "../dispatchkit/register_function.hpp" +#include "../language/chaiscript_common.hpp" +namespace chaiscript::utility { + /// Single step command for registering a class with ChaiScript + /// + /// \param[in,out] t_module Model to add class to + /// \param[in] t_class_name Name of the class being registered + /// \param[in] t_constructors Vector of constructors to add + /// \param[in] t_funcs Vector of methods to add + /// + /// \example Adding a basic class to ChaiScript in one step + /// + /// \code + /// chaiscript::utility::add_class(*m, + /// "test", + /// { constructor(), + /// constructor() }, + /// { {fun(&test::function), "function"}, + /// {fun(&test::function2), "function2"}, + /// {fun(&test::function3), "function3"}, + /// {fun(static_cast(&test::function_overload)), "function_overload" }, + /// {fun(static_cast(&test::function_overload)), "function_overload" }, + /// {fun(static_cast(&test::operator=)), "=" } + /// } + /// ); + /// + template + void add_class(ModuleType &t_module, + const std::string &t_class_name, + const std::vector &t_constructors, + const std::vector> &t_funcs) { + t_module.add(chaiscript::user_type(), t_class_name); -namespace chaiscript -{ - namespace utility - { + for (const chaiscript::Proxy_Function &ctor : t_constructors) { + t_module.add(ctor, t_class_name); + } - /// Single step command for registering a class with ChaiScript - /// - /// \param[in,out] t_module Model to add class to - /// \param[in] t_class_name Name of the class being registered - /// \param[in] t_constructors Vector of constructors to add - /// \param[in] t_funcs Vector of methods to add - /// - /// \example Adding a basic class to ChaiScript in one step - /// - /// \code - /// chaiscript::utility::add_class(*m, - /// "test", - /// { constructor(), - /// constructor() }, - /// { {fun(&test::function), "function"}, - /// {fun(&test::function2), "function2"}, - /// {fun(&test::function3), "function3"}, - /// {fun(static_cast(&test::function_overload)), "function_overload" }, - /// {fun(static_cast(&test::function_overload)), "function_overload" }, - /// {fun(static_cast(&test::operator=)), "=" } - /// } - /// ); - /// - template - void add_class(ModuleType &t_module, - const std::string &t_class_name, - const std::vector &t_constructors, - const std::vector> &t_funcs) - { - t_module.add(chaiscript::user_type(), t_class_name); - - for(const chaiscript::Proxy_Function &ctor: t_constructors) - { - t_module.add(ctor, t_class_name); - } - - for(const auto &fun: t_funcs) - { - t_module.add(fun.first, fun.second); - } - } - - template - typename std::enable_if::value, void>::type - add_class(ModuleType &t_module, - const std::string &t_class_name, - const std::vector::type, std::string>> &t_constants - ) - { - t_module.add(chaiscript::user_type(), t_class_name); - - t_module.add(chaiscript::constructor(), t_class_name); - t_module.add(chaiscript::constructor(), t_class_name); - - using namespace chaiscript::bootstrap::operators; - equal(t_module); - not_equal(t_module); - assign(t_module); - - t_module.add(chaiscript::fun([](const Enum &e, const int &i) { return e == i; }), "=="); - t_module.add(chaiscript::fun([](const int &i, const Enum &e) { return i == e; }), "=="); - - for (const auto &constant : t_constants) - { - t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second); - } - } - - template - typename std::enable_if::value, void>::type - add_class(ModuleType &t_module, - const std::string &t_class_name, - const std::vector> &t_constants - ) - { - t_module.add(chaiscript::user_type(), t_class_name); - - t_module.add(chaiscript::constructor(), t_class_name); - t_module.add(chaiscript::constructor(), t_class_name); - - using namespace chaiscript::bootstrap::operators; - equal(t_module); - not_equal(t_module); - assign(t_module); - - for (const auto &constant : t_constants) - { - t_module.add_global_const(chaiscript::const_var(EnumClass(constant.first)), constant.second); - } - } + for (const auto &fun : t_funcs) { + t_module.add(fun.first, fun.second); + } } -} + + template + typename std::enable_if::value, void>::type + add_class(ModuleType &t_module, + const std::string &t_class_name, + const std::vector::type, std::string>> &t_constants) { + t_module.add(chaiscript::user_type(), t_class_name); + + t_module.add(chaiscript::constructor(), t_class_name); + t_module.add(chaiscript::constructor(), t_class_name); + + using namespace chaiscript::bootstrap::operators; + equal(t_module); + not_equal(t_module); + assign(t_module); + + t_module.add(chaiscript::fun([](const Enum &e, const int &i) { return e == i; }), "=="); + t_module.add(chaiscript::fun([](const int &i, const Enum &e) { return i == e; }), "=="); + + for (const auto &constant : t_constants) { + t_module.add_global_const(chaiscript::const_var(Enum(constant.first)), constant.second); + } + } + + template + typename std::enable_if::value, void>::type + add_class(ModuleType &t_module, const std::string &t_class_name, const std::vector> &t_constants) { + t_module.add(chaiscript::user_type(), t_class_name); + + t_module.add(chaiscript::constructor(), t_class_name); + t_module.add(chaiscript::constructor(), t_class_name); + + using namespace chaiscript::bootstrap::operators; + equal(t_module); + not_equal(t_module); + assign(t_module); + + for (const auto &constant : t_constants) { + t_module.add_global_const(chaiscript::const_var(EnumClass(constant.first)), constant.second); + } + } +} // namespace chaiscript::utility #endif - diff --git a/license.txt b/license.txt index 8e6a6b98..8673d971 100644 --- a/license.txt +++ b/license.txt @@ -1,7 +1,6 @@ BSD-3-Clause License -Copyright 2009-2016 Jason Turner -Copyright 2009-2012 Jonathan Turner. +Copyright 2009-2018 Jason Turner All Rights Reserved. Redistribution and use in source and binary forms, with or without diff --git a/performance_tests/profile_cpp_calls_2.cpp b/performance_tests/profile_cpp_calls_2.cpp index 0424654b..3cf355b3 100644 --- a/performance_tests/profile_cpp_calls_2.cpp +++ b/performance_tests/profile_cpp_calls_2.cpp @@ -5,9 +5,8 @@ double f(const std::string &, double, bool) noexcept { return .0; } -int main() -{ - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); +int main() { + chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&f), "f"); @@ -16,5 +15,4 @@ int main() f("str", 1.2, false); } )"); - } diff --git a/performance_tests/profile_fun_wrappers.cpp b/performance_tests/profile_fun_wrappers.cpp index fb96f483..f71aae3f 100644 --- a/performance_tests/profile_fun_wrappers.cpp +++ b/performance_tests/profile_fun_wrappers.cpp @@ -5,16 +5,14 @@ double f(const std::string &, double, bool) noexcept { return .0; } -int main() -{ - chaiscript::ChaiScript chai(chaiscript::Std_Lib::library()); +int main() { + chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&f), "f"); - const auto f = chai.eval>(R"(fun(){ f("str", 1.2, false); })"); + const auto f = chai.eval>(R"(fun(){ f("str", 1.2, false); })"); for (int i = 0; i < 100000; ++i) { f(); } - } diff --git a/readme.md b/readme.md index 8323a128..88f8a516 100644 --- a/readme.md +++ b/readme.md @@ -29,7 +29,7 @@ Introduction ChaiScript is one of the only embedded scripting language designed from the ground up to directly target C++ and take advantage of modern C++ development -techniques, working with the developer like he expects it to work. Being a +techniques, working with the developer how they would expect it to work. Being a native C++ application, it has some advantages over existing embedded scripting languages: @@ -48,6 +48,19 @@ templates. It has been tested with gcc 4.9 and clang 3.6 (with libcxx). For more information see the build [dashboard](http://chaiscript.com/ChaiScript-BuildResults/index.html). +Installation using vcpkg +======================== + +You can download and install ChaiScript using the [vcpkg](https://github.com/Microsoft/vcpkg/) dependency manager: + + git clone https://github.com/Microsoft/vcpkg.git + cd vcpkg + ./bootstrap-vcpkg.sh + ./vcpkg integrate install + vcpkg install chaiscript + +The ChaiScript port in vcpkg is kept up to date by Microsoft team members and community contributors. If the version is out of date, please [create an issue or pull request](https://github.com/Microsoft/vcpkg) on the vcpkg repository. + Usage ===== diff --git a/releasenotes.md b/releasenotes.md index c4ab63be..a622566e 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -1,6 +1,13 @@ Notes: ======= -Current Version: 6.1.0 +Current Version: 6.1.1 + +### Changes since 6.1.0 + + * Handle the returning of `&` to `*` types. This specifically comes up with `std::vector` and similar containers + * Update CMake to use `LIBDIR` instead of `lib` #502 by @guoyunhe + * Add documentation for installing ChaiScript with vcpkg #500 by @grdowns + * Fix warning for implicit 'this' lambda capture in C++20 #495 by @Josh-Thompson ### Changes since 6.0.0 @@ -14,7 +21,6 @@ Current Version: 6.1.0 * Support for C++17 compilers! * Support for UTF8 BOM #439 @AlekMosingiewicz @MarioLiebisch - ### Changes since 5.8.6 *6.0.0 is a massive rework compared to 5.x. It now requires a C++14 enabled compiler* diff --git a/samples/factory.cpp b/samples/factory.cpp index 976868b5..6ddb77b4 100644 --- a/samples/factory.cpp +++ b/samples/factory.cpp @@ -1,61 +1,53 @@ #include -class Entity -{ - public: - int width; - int height; - int x; - int y; - std::string name; +class Entity { +public: + int width; + int height; + int x; + int y; + std::string name; - std::function updater; + std::function updater; - Entity(const int t_width, const int t_height, const int t_x, const int t_y, std::string t_name) - : width(t_width), height(t_height), x(t_x), y(t_y), name(std::move(t_name)) - { - } + Entity(const int t_width, const int t_height, const int t_x, const int t_y, std::string t_name) + : width(t_width) + , height(t_height) + , x(t_x) + , y(t_y) + , name(std::move(t_name)) { + } }; -class Factory -{ - public: - // we may as well pass the parameters for the entity to the factory method, this does the initialization - // in one step. - Entity *make_entity(const int width, const int height, const int x, const int y, const std::string &name) - { - auto entity = entities.insert({name, Entity{width, height, x, y, name}}); - return &(entity.first->second); - } +class Factory { +public: + // we may as well pass the parameters for the entity to the factory method, this does the initialization + // in one step. + Entity *make_entity(const int width, const int height, const int x, const int y, const std::string &name) { + auto entity = entities.insert({name, Entity{width, height, x, y, name}}); + return &(entity.first->second); + } - Entity *get_entity(const std::string &name) - { - return &entities.at(name); - } + Entity *get_entity(const std::string &name) { return &entities.at(name); } - - // loop over all entities and all their updater function (if it exists) - void update_entities() - { - for (auto &entity : entities) - { - if (entity.second.updater) { - entity.second.updater(entity.second); - } + // loop over all entities and all their updater function (if it exists) + void update_entities() { + for (auto &entity : entities) { + if (entity.second.updater) { + entity.second.updater(entity.second); } } + } - - private: - // we cannot store the entities in a std::vector if we want to return a pointer to them, - // because a vector automatically resizing itself can invalidate the pointer that was returned. - // using a map guarantees that the memory assigned to the entity will never change, plus - // lets us easily look up an entity by name - std::map entities; +private: + // we cannot store the entities in a std::vector if we want to return a pointer to them, + // because a vector automatically resizing itself can invalidate the pointer that was returned. + // using a map guarantees that the memory assigned to the entity will never change, plus + // lets us easily look up an entity by name + std::map entities; }; -int main() -{ +int main() { chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&Entity::width), "width"); @@ -71,7 +63,6 @@ int main() chai.add(chaiscript::fun(&Factory::update_entities), "update_entities"); chai.add(chaiscript::user_type(), "Factory"); // this isn't strictly necessary but makes error messages nicer - Factory f; chai.add(chaiscript::var(&f), "f"); @@ -92,11 +83,5 @@ int main() print(f.get_entity("entity3").x == 20) // this one has no updater, so it stays the same )""; - chai.eval(script); - - - } - - diff --git a/samples/fun_call_performance.cpp b/samples/fun_call_performance.cpp index 31fa606c..da5417ce 100644 --- a/samples/fun_call_performance.cpp +++ b/samples/fun_call_performance.cpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include #include #include @@ -19,43 +18,38 @@ #include #ifdef READLINE_AVAILABLE -#include #include +#include #else char *mystrdup(const char *s) { size_t len = strlen(s); // Space for length plus nul - char *d = static_cast(malloc(len + 1)); - if (d == nullptr) return nullptr; // No memory + char *d = static_cast(malloc(len + 1)); + if (d == nullptr) + return nullptr; // No memory #ifdef CHAISCRIPT_MSVC - strcpy_s(d, len + 1, s); // Copy the characters + strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d, s, len); // Copy the characters + strncpy(d, s, len); // Copy the characters #endif d[len] = '\0'; - return d; // Return the new string + return d; // Return the new string } -char* readline(const char* p) -{ +char *readline(const char *p) { std::string retval; std::cout << p; std::getline(std::cin, retval); return std::cin.eof() ? nullptr : mystrdup(retval.c_str()); } - -void add_history(const char*){} -void using_history(){} +void add_history(const char *) {} +void using_history() {} #endif - - -void *cast_module_symbol(std::vector(*t_path)()) -{ - union cast_union - { - std::vector(*in_ptr)(); +void *cast_module_symbol(std::vector (*t_path)()) { + union cast_union { + std::vector (*in_ptr)(); void *out_ptr; }; @@ -64,12 +58,11 @@ void *cast_module_symbol(std::vector(*t_path)()) return c.out_ptr; } -std::vector default_search_paths() -{ +std::vector default_search_paths() { std::vector paths; #ifndef CHAISCRIPT_NO_DYNLOAD -#ifdef CHAISCRIPT_WINDOWS // force no unicode +#ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; int size = GetModuleFileNameA(0, path, sizeof(path) - 1); @@ -77,14 +70,12 @@ std::vector default_search_paths() size_t lastslash = exepath.rfind('\\'); size_t secondtolastslash = exepath.rfind('\\', lastslash - 1); - if (lastslash != std::string::npos) - { + if (lastslash != std::string::npos) { paths.push_back(exepath.substr(0, lastslash)); } - if (secondtolastslash != std::string::npos) - { - return{ exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\" }; + if (secondtolastslash != std::string::npos) { + return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"}; } #else @@ -93,29 +84,23 @@ std::vector default_search_paths() std::vector buf(2048); ssize_t size = -1; - if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) > 0) - { + if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) > 0) { exepath = std::string(&buf.front(), static_cast(size)); } - if (exepath.empty()) - { - if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) > 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) > 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { - if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) > 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) > 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { + if (exepath.empty()) { Dl_info rInfo; memset(&rInfo, 0, sizeof(rInfo)); if (!dladdr(cast_module_symbol(&default_search_paths), &rInfo) || !rInfo.dli_fname) { @@ -128,13 +113,11 @@ std::vector default_search_paths() size_t lastslash = exepath.rfind('/'); size_t secondtolastslash = exepath.rfind('/', lastslash - 1); - if (lastslash != std::string::npos) - { + if (lastslash != std::string::npos) { paths.push_back(exepath.substr(0, lastslash)); } - if (secondtolastslash != std::string::npos) - { + if (secondtolastslash != std::string::npos) { paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif @@ -145,46 +128,40 @@ std::vector default_search_paths() void help(int n) { if (n >= 0) { - std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press ." << std::endl; - std::cout << "Additionally, you can inspect the runtime system using:" << std::endl; - std::cout << " dump_system() - outputs all functions registered to the system" << std::endl; - std::cout << " dump_object(x) - dumps information about the given symbol" << std::endl; - } - else { - std::cout << "usage : chai [option]+" << std::endl; - std::cout << "option:" << std::endl; - std::cout << " -h | --help" << std::endl; - std::cout << " -i | --interactive" << std::endl; - std::cout << " -c | --command cmd" << std::endl; - std::cout << " -v | --version" << std::endl; - std::cout << " - --stdin" << std::endl; - std::cout << " filepath" << std::endl; + std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; + std::cout << "Additionally, you can inspect the runtime system using:\n"; + std::cout << " dump_system() - outputs all functions registered to the system\n"; + std::cout << " dump_object(x) - dumps information about the given symbol\n"; + } else { + std::cout << "usage : chai [option]+\n"; + std::cout << "option:\n"; + std::cout << " -h | --help\n"; + std::cout << " -i | --interactive\n"; + std::cout << " -c | --command cmd\n"; + std::cout << " -v | --version\n"; + std::cout << " - --stdin\n"; + std::cout << " filepath\n"; } } -std::string helloWorld(const std::string &t_name) -{ +std::string helloWorld(const std::string &t_name) { return "Hello " + t_name + "!"; } -bool throws_exception(const std::function &f) -{ +bool throws_exception(const std::function &f) { try { f(); - } - catch (...) { + } catch (...) { return true; } return false; } -chaiscript::exception::eval_error get_eval_error(const std::function &f) -{ +chaiscript::exception::eval_error get_eval_error(const std::function &f) { try { f(); - } - catch (const chaiscript::exception::eval_error &e) { + } catch (const chaiscript::exception::eval_error &e) { return e; } @@ -200,13 +177,11 @@ std::string get_next_command() { std::string val(input_raw); size_t pos = val.find_first_not_of("\t \n"); - if (pos != std::string::npos) - { + if (pos != std::string::npos) { val.erase(0, pos); } pos = val.find_last_not_of("\t \n"); - if (pos != std::string::npos) - { + if (pos != std::string::npos) { val.erase(pos + 1, std::string::npos); } @@ -215,11 +190,7 @@ std::string get_next_command() { ::free(input_raw); } } - if (retval == "quit" - || retval == "exit" - || retval == "help" - || retval == "version") - { + if (retval == "quit" || retval == "exit" || retval == "help" || retval == "version") { retval += "(0)"; } return retval; @@ -231,8 +202,7 @@ void myexit(int return_val) { exit(return_val); } -void interactive(chaiscript::ChaiScript& chai) -{ +void interactive(chaiscript::ChaiScript &chai) { using_history(); for (;;) { @@ -241,32 +211,28 @@ void interactive(chaiscript::ChaiScript& chai) // evaluate input chaiscript::Boxed_Value val = chai.eval(input); - //Then, we try to print the result of the evaluation to the user + // Then, we try to print the result of the evaluation to the user if (!val.get_type_info().bare_equal(chaiscript::user_type())) { try { - std::cout << chai.eval >("to_string")(val) << std::endl; - } - catch (...) {} //If we can't, do nothing + std::cout << chai.eval>("to_string")(val) << '\n'; + } catch (...) { + } // If we can't, do nothing } - } - catch (const chaiscript::exception::eval_error &ee) { + } catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); if (ee.call_stack.size() > 0) { std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } - std::cout << std::endl; - } - catch (const std::exception &e) { + std::cout << '\n'; + } catch (const std::exception &e) { std::cout << e.what(); std::cout << std::endl; } } } -int main(int argc, char *argv[]) -{ - - // Disable deprecation warning for getenv call. +int main(int argc, char *argv[]) { +// Disable deprecation warning for getenv call. #ifdef CHAISCRIPT_MSVC #pragma warning(push) #pragma warning(disable : 4996) @@ -281,8 +247,7 @@ int main(int argc, char *argv[]) std::vector usepaths; usepaths.push_back(""); - if (usepath) - { + if (usepath) { usepaths.push_back(usepath); } @@ -290,12 +255,11 @@ int main(int argc, char *argv[]) std::vector searchpaths = default_search_paths(); modulepaths.insert(modulepaths.end(), searchpaths.begin(), searchpaths.end()); modulepaths.push_back(""); - if (modulepath) - { + if (modulepath) { modulepaths.push_back(modulepath); } - //chaiscript::ChaiScript chai(modulepaths, usepaths); + // chaiscript::ChaiScript chai(modulepaths, usepaths); chaiscript::ChaiScript chai(usepaths); chai.add(chaiscript::fun(&myexit), "exit"); @@ -308,8 +272,7 @@ int main(int argc, char *argv[]) clock_t begin = clock(); - for (int i = 0; i < 1000; i++) - { + for (int i = 0; i < 1000; i++) { std::string str = helloWorld("Bob12345"); fwrite(str.c_str(), 1, str.size(), stdout); } @@ -317,17 +280,17 @@ int main(int argc, char *argv[]) clock_t end = clock(); double elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; - //begin = clock(); + // begin = clock(); ////for (int i = 0; i < 1000; i++) ////{ //// chai.eval("puts(helloWorld(\"Bob12345\"));"); ////} - //chai.eval_file("E:\\C++\\ChaiScript - 5.4.0\\samples\forx.chai"); + // chai.eval_file("E:\\C++\\ChaiScript - 5.4.0\\samples\forx.chai"); - //end = clock(); - //elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; - //printf("**MyProgram::time= %lf\n", elapsed_secs); + // end = clock(); + // elapsed_secs = double(end - begin) / CLOCKS_PER_SEC; + // printf("**MyProgram::time= %lf\n", elapsed_secs); for (int i = 0; i < argc; ++i) { if (i == 0 && argc > 1) { @@ -337,69 +300,64 @@ int main(int argc, char *argv[]) std::string arg(i ? argv[i] : "--interactive"); enum { - eInteractive - , eCommand - , eFile - } mode = eCommand; + eInteractive, + eCommand, + eFile + } mode + = eCommand; if (arg == "-c" || arg == "--command") { if ((i + 1) >= argc) { std::cout << "insufficient input following " << arg << std::endl; return EXIT_FAILURE; - } - else { + } else { arg = argv[++i]; } - } - else if (arg == "-" || arg == "--stdin") { + } else if (arg == "-" || arg == "--stdin") { arg = ""; std::string line; while (std::getline(std::cin, line)) { arg += line + '\n'; } - } - else if (arg == "-v" || arg == "--version") { + } else if (arg == "-v" || arg == "--version") { arg = "version()"; - } - else if (arg == "-h" || arg == "--help") { + } else if (arg == "-h" || arg == "--help") { arg = "help(-1)"; - } - else if (arg == "-i" || arg == "--interactive") { + } else if (arg == "-i" || arg == "--interactive") { mode = eInteractive; - } - else if (arg.find('-') == 0) { + } else if (arg.find('-') == 0) { std::cout << "unrecognised argument " << arg << std::endl; return EXIT_FAILURE; - } - else { + } else { mode = eFile; } chaiscript::Boxed_Value val; try { switch (mode) { - case eInteractive: interactive(chai); break; - case eCommand: val = chai.eval(arg); break; + case eInteractive: + interactive(chai); + break; + case eCommand: + val = chai.eval(arg); + break; case eFile: { - begin = clock(); + begin = clock(); - val = chai.eval_file(arg); - - end = clock(); - double elapsed_secs1 = double(end - begin) / CLOCKS_PER_SEC; - printf("**C++::time= %.10f\n", elapsed_secs); - printf("**ChaiScript::time= %.10f\n", elapsed_secs1); - break; - } + val = chai.eval_file(arg); + end = clock(); + double elapsed_secs1 = double(end - begin) / CLOCKS_PER_SEC; + printf("**C++::time= %.10f\n", elapsed_secs); + printf("**ChaiScript::time= %.10f\n", elapsed_secs1); + break; + } } - } - catch (const chaiscript::exception::eval_error &ee) { + } catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.pretty_print(); std::cout << std::endl; return EXIT_FAILURE; - } - catch (std::exception &e) { + } catch (std::exception &e) { std::cout << e.what() << std::endl; return EXIT_FAILURE; } @@ -407,4 +365,3 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } - diff --git a/samples/inheritance.cpp b/samples/inheritance.cpp index 1c9ddb63..616d773f 100644 --- a/samples/inheritance.cpp +++ b/samples/inheritance.cpp @@ -1,79 +1,66 @@ #include -class BaseClass -{ - public: - BaseClass() - { +class BaseClass { +public: + BaseClass() = default; + + BaseClass(const BaseClass &) = default; + + virtual ~BaseClass() = default; + + virtual std::string doSomething(float, double) const = 0; + + void setValue(const std::string &t_val) { + if (validateValue(t_val)) { + m_value = t_val; } + } - BaseClass(const BaseClass &) = default; + std::string getValue() const { return m_value; } - virtual ~BaseClass() {} +protected: + virtual bool validateValue(const std::string &t_val) = 0; - virtual std::string doSomething(float, double) const = 0; - - - void setValue(const std::string &t_val) { - if (validateValue(t_val)) - { - m_value = t_val; - } - } - - std::string getValue() const { - return m_value; - } - - protected: - virtual bool validateValue(const std::string &t_val) = 0; - - private: - std::string m_value; +private: + std::string m_value; }; -class ChaiScriptDerived : public BaseClass -{ - public: - ChaiScriptDerived(const std::vector &t_funcs) - { - // using the range-checked .at() methods to give us an exception - // instead of a crash if the user passed in too-few params - tie(t_funcs.at(0), m_doSomethingImpl); - tie(t_funcs.at(1), m_validateValueImpl); - } +class ChaiScriptDerived : public BaseClass { +public: + ChaiScriptDerived(const std::vector &t_funcs) { + // using the range-checked .at() methods to give us an exception + // instead of a crash if the user passed in too-few params + tie(t_funcs.at(0), m_doSomethingImpl); + tie(t_funcs.at(1), m_validateValueImpl); + } - std::string doSomething(float f, double d) const override - { - assert(m_doSomethingImpl); - return m_doSomethingImpl(*this, f, d); - } + std::string doSomething(float f, double d) const override { + assert(m_doSomethingImpl); + return m_doSomethingImpl(*this, f, d); + } - protected: - bool validateValue(const std::string &t_val) override - { - assert(m_validateValueImpl); - return m_validateValueImpl(*this, t_val); - } +protected: + bool validateValue(const std::string &t_val) override { + assert(m_validateValueImpl); + return m_validateValueImpl(*this, t_val); + } - private: - template - void tie(const chaiscript::Boxed_Value &t_func, Param &t_param) - { - t_param = chaiscript::boxed_cast(t_func); - } +private: + template + void tie(const chaiscript::Boxed_Value &t_func, Param &t_param) { + t_param = chaiscript::boxed_cast(t_func); + } - std::function m_doSomethingImpl; - std::function m_validateValueImpl; + std::function m_doSomethingImpl; + std::function m_validateValueImpl; }; -int main() -{ +int main() { chaiscript::ChaiScript chai; chai.add(chaiscript::fun(&BaseClass::doSomething), "doSomething"); chai.add(chaiscript::fun(&BaseClass::setValue), "setValue"); chai.add(chaiscript::fun(&BaseClass::getValue), "getValue"); - chai.add(chaiscript::constructor &)>(), "ChaiScriptDerived"); + chai.add(chaiscript::constructor &)>(), "ChaiScriptDerived"); chai.add(chaiscript::base_class()); chai.add(chaiscript::user_type(), "BaseClass"); chai.add(chaiscript::user_type(), "ChaiScriptDerived"); @@ -105,8 +92,8 @@ int main() )""; chai.eval(script); - - BaseClass &myderived = chai.eval("myderived"); + + BaseClass &myderived = chai.eval("myderived"); // at this point in the code myderived is both a ChaiScript variable and a C++ variable. In both cases // it is a derivation of BaseClass, and the implementation is provided via ChaiScript functors @@ -121,7 +108,6 @@ int main() myderived.setValue("12345"); assert(myderived.getValue() == "1234"); - chai.eval(R"(myderived.setValue("new"))"); // set the value via chaiscript assert(myderived.getValue() == "new"); @@ -131,5 +117,3 @@ int main() // The whole process is fully orthogonal } - - diff --git a/samples/memory_leak_test.cpp b/samples/memory_leak_test.cpp index 513656b7..8cea1cdb 100644 --- a/samples/memory_leak_test.cpp +++ b/samples/memory_leak_test.cpp @@ -3,12 +3,10 @@ #include #ifdef READLINE_AVAILABLE -#include #include +#include #endif - - std::string get_next_command() { #ifdef READLINE_AVAILABLE char *input_raw; @@ -23,42 +21,32 @@ std::string get_next_command() { #endif } -void function(void) -{ +void function(void) { // do nothing } -class test -{ +class test { chaiscript::ChaiScript chai; chaiscript::ChaiScript::State backupState = chai.get_state(); - public: - void ResetState() - { +public: + void ResetState() { chai.set_state(backupState); - chai.add(chaiscript::fun(&function),"Whatever()"); + chai.add(chaiscript::fun(&function), "Whatever()"); } - void RunFile(std::string sFile) - { + void RunFile(std::string sFile) { try { chaiscript::Boxed_Value val = chai.eval_file(sFile); - } - catch (std::exception &e) { + } catch (std::exception &e) { std::cout << e.what() << '\n'; } } - }; - - int main(int /*argc*/, char * /*argv*/[]) { - test myChai; - std::string command = ""; // @@ -70,12 +58,11 @@ int main(int /*argc*/, char * /*argv*/[]) { // scenario3 - RunFile gets in changing intervals: memory usage goes up and down, but never as // low as in case 1 scenario3 : - while(command != "quit") - { - for(int i = 1; i < 200; i++) + while (command != "quit") { + for (int i = 1; i < 200; i++) myChai.ResetState(); - if(command == "runfile") + if (command == "runfile") myChai.RunFile("Test.chai"); command = get_next_command(); diff --git a/samples/test_num_exceptions.cpp b/samples/test_num_exceptions.cpp index 8347c4a0..59d816fa 100644 --- a/samples/test_num_exceptions.cpp +++ b/samples/test_num_exceptions.cpp @@ -2,15 +2,12 @@ #include #include -int main( int /*argc*/ , char * /*argv*/[] ) -{ +int main(int /*argc*/, char * /*argv*/[]) { chaiscript::ChaiScript ch; - - try - { - static const char script[ ] = - R""( + try { + static const char script[] = + R""( class Rectangle { @@ -21,12 +18,9 @@ int main( int /*argc*/ , char * /*argv*/[] ) )""; - - ch.eval( script ); - } - catch ( const std::exception &e ) - { - printf( " >>> Exception thrown: %s \n" , e.what( ) ); + ch.eval(script); + } catch (const std::exception &e) { + printf(" >>> Exception thrown: %s \n", e.what()); } return 1; diff --git a/src/chaiscript_stdlib_module.cpp b/src/chaiscript_stdlib_module.cpp index d4ea13e8..f7224d40 100644 --- a/src/chaiscript_stdlib_module.cpp +++ b/src/chaiscript_stdlib_module.cpp @@ -1,7 +1,6 @@ #include - // MSVC doesn't like that we are using C++ return types from our C declared module // but this is the best way to do it for cross platform compatibility #ifdef CHAISCRIPT_MSVC @@ -14,13 +13,10 @@ #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" #endif - -CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_chaiscript_stdlib() -{ +CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_chaiscript_stdlib() { return chaiscript::Std_Lib::library(); } - #ifdef __llvm__ #pragma clang diagnostic pop #endif diff --git a/src/libfuzzer_client.cpp b/src/libfuzzer_client.cpp index 28927ee1..39ebe2f4 100644 --- a/src/libfuzzer_client.cpp +++ b/src/libfuzzer_client.cpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include #include #include @@ -16,49 +15,45 @@ #define _CRT_SECURE_NO_WARNINGS #endif -#include #include "../static_libs/chaiscript_parser.hpp" #include "../static_libs/chaiscript_stdlib.hpp" +#include #include "sha3.h" #ifdef READLINE_AVAILABLE -#include #include +#include #else -char *mystrdup (const char *s) { +char *mystrdup(const char *s) { size_t len = strlen(s); // Space for length plus nul - char *d = static_cast(malloc (len+1)); - if (d == nullptr) { return nullptr; } // No memory + char *d = static_cast(malloc(len + 1)); + if (d == nullptr) { + return nullptr; + } // No memory #ifdef CHAISCRIPT_MSVC - strcpy_s(d, len+1, s); // Copy the characters + strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d,s,len); // Copy the characters + strncpy(d, s, len); // Copy the characters #endif d[len] = '\0'; - return d; // Return the new string + return d; // Return the new string } -char* readline(const char* p) -{ +char *readline(const char *p) { std::string retval; - std::cout << p ; + std::cout << p; std::getline(std::cin, retval); return std::cin.eof() ? nullptr : mystrdup(retval.c_str()); } - -void add_history(const char* /*unused*/){} -void using_history(){} +void add_history(const char * /*unused*/) {} +void using_history() {} #endif - - -void *cast_module_symbol(std::vector (*t_path)()) -{ - union cast_union - { +void *cast_module_symbol(std::vector (*t_path)()) { + union cast_union { std::vector (*in_ptr)(); void *out_ptr; }; @@ -68,26 +63,23 @@ void *cast_module_symbol(std::vector (*t_path)()) return c.out_ptr; } -std::vector default_search_paths() -{ +std::vector default_search_paths() { std::vector paths; #ifndef CHAISCRIPT_NO_DYNLOAD -#ifdef CHAISCRIPT_WINDOWS // force no unicode +#ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; - int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); + int size = GetModuleFileNameA(nullptr, path, sizeof(path) - 1); std::string exepath(path, size); size_t lastslash = exepath.rfind('\\'); size_t secondtolastslash = exepath.rfind('\\', lastslash - 1); - if (lastslash != std::string::npos) - { + if (lastslash != std::string::npos) { paths.push_back(exepath.substr(0, lastslash)); } - if (secondtolastslash != std::string::npos) - { + if (secondtolastslash != std::string::npos) { return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"}; } #else @@ -97,32 +89,26 @@ std::vector default_search_paths() std::vector buf(2048); ssize_t size = -1; - if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) - { + if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } - if (exepath.empty()) - { - if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { - if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { + if (exepath.empty()) { Dl_info rInfo; - memset( &rInfo, 0, sizeof(rInfo) ); - if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) { + memset(&rInfo, 0, sizeof(rInfo)); + if (dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr) { return paths; } @@ -132,13 +118,11 @@ std::vector default_search_paths() size_t lastslash = exepath.rfind('/'); size_t secondtolastslash = exepath.rfind('/', lastslash - 1); - if (lastslash != std::string::npos) - { - paths.push_back(exepath.substr(0, lastslash+1)); + if (lastslash != std::string::npos) { + paths.push_back(exepath.substr(0, lastslash + 1)); } - if (secondtolastslash != std::string::npos) - { + if (secondtolastslash != std::string::npos) { paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif @@ -148,25 +132,24 @@ std::vector default_search_paths() } void help(int n) { - if ( n >= 0 ) { + if (n >= 0) { std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; std::cout << "Additionally, you can inspect the runtime system using:\n"; std::cout << " dump_system() - outputs all functions registered to the system\n"; std::cout << " dump_object(x) - dumps information about the given symbol\n"; } else { std::cout << "usage : chai [option]+\n"; - std::cout << "option:" << '\n'; - std::cout << " -h | --help" << '\n'; - std::cout << " -i | --interactive" << '\n'; - std::cout << " -c | --command cmd" << '\n'; - std::cout << " -v | --version" << '\n'; - std::cout << " - --stdin" << '\n'; - std::cout << " filepath" << '\n'; + std::cout << "option:" << '\n'; + std::cout << " -h | --help" << '\n'; + std::cout << " -i | --interactive" << '\n'; + std::cout << " -c | --command cmd" << '\n'; + std::cout << " -v | --version" << '\n'; + std::cout << " - --stdin" << '\n'; + std::cout << " filepath" << '\n'; } } -bool throws_exception(const std::function &f) -{ +bool throws_exception(const std::function &f) { try { f(); } catch (...) { @@ -176,8 +159,7 @@ bool throws_exception(const std::function &f) return false; } -chaiscript::exception::eval_error get_eval_error(const std::function &f) -{ +chaiscript::exception::eval_error get_eval_error(const std::function &f) { try { f(); } catch (const chaiscript::exception::eval_error &e) { @@ -189,21 +171,19 @@ chaiscript::exception::eval_error get_eval_error(const std::function &f std::string get_next_command() { std::string retval("quit"); - if ( ! std::cin.eof() ) { + if (!std::cin.eof()) { char *input_raw = readline("eval> "); - if ( input_raw != nullptr ) { + if (input_raw != nullptr) { add_history(input_raw); std::string val(input_raw); size_t pos = val.find_first_not_of("\t \n"); - if (pos != std::string::npos) - { + if (pos != std::string::npos) { val.erase(0, pos); } pos = val.find_last_not_of("\t \n"); - if (pos != std::string::npos) - { - val.erase(pos+1, std::string::npos); + if (pos != std::string::npos) { + val.erase(pos + 1, std::string::npos); } retval = val; @@ -211,11 +191,7 @@ std::string get_next_command() { ::free(input_raw); } } - if( retval == "quit" - || retval == "exit" - || retval == "help" - || retval == "version") - { + if (retval == "quit" || retval == "exit" || retval == "help" || retval == "version") { retval += "(0)"; } return retval; @@ -227,8 +203,7 @@ void myexit(int return_val) { exit(return_val); } -void interactive(chaiscript::ChaiScript_Basic& chai) -{ +void interactive(chaiscript::ChaiScript_Basic &chai) { using_history(); for (;;) { @@ -237,30 +212,27 @@ void interactive(chaiscript::ChaiScript_Basic& chai) // evaluate input chaiscript::Boxed_Value val = chai.eval(input); - //Then, we try to print the result of the evaluation to the user + // Then, we try to print the result of the evaluation to the user if (!val.get_type_info().bare_equal(chaiscript::user_type())) { try { - std::cout << chai.eval >("to_string")(val) << '\n'; - } - catch (...) {} //If we can't, do nothing + std::cout << chai.eval>("to_string")(val) << '\n'; + } catch (...) { + } // If we can't, do nothing } - } - catch (const chaiscript::exception::eval_error &ee) { + } catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); - if ( !ee.call_stack.empty() ) { + if (!ee.call_stack.empty()) { std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << '\n'; - } - catch (const std::exception &e) { + } catch (const std::exception &e) { std::cout << e.what(); std::cout << '\n'; } } } -double now() -{ +double now() { using namespace std::chrono; auto now = high_resolution_clock::now(); return duration_cast>(now.time_since_epoch()).count(); @@ -269,7 +241,7 @@ double now() extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { chaiscript::ChaiScript chai; - chai.eval( R"chaiscript( + chai.eval(R"chaiscript( def assert_equal(x, y) { if (x == y) @@ -339,7 +311,8 @@ def assert_throws(desc, x) std::ofstream ofs("BOXED_VALUE/" + sha); ofs << input; } catch (const chaiscript::exception::load_module_error &e) { - std::cout << "Unhandled module load error\n" << e.what() << '\n'; + std::cout << "Unhandled module load error\n" + << e.what() << '\n'; } catch (const std::exception &) { std::ofstream ofs("STD_EXCEPTION/" + sha); ofs << input; @@ -350,5 +323,3 @@ def assert_throws(desc, x) return 0; } - - diff --git a/src/main.cpp b/src/main.cpp index b5d55520..6ec746fb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include #include #include @@ -16,48 +15,43 @@ #define _CRT_SECURE_NO_WARNINGS #endif -#include #include "../static_libs/chaiscript_parser.hpp" #include "../static_libs/chaiscript_stdlib.hpp" - +#include #ifdef READLINE_AVAILABLE -#include #include +#include #else -char *mystrdup (const char *s) { +char *mystrdup(const char *s) { size_t len = strlen(s); // Space for length plus nul - char *d = static_cast(malloc (len+1)); - if (d == nullptr) { return nullptr; } // No memory + char *d = static_cast(malloc(len + 1)); + if (d == nullptr) { + return nullptr; + } // No memory #ifdef CHAISCRIPT_MSVC - strcpy_s(d, len+1, s); // Copy the characters + strcpy_s(d, len + 1, s); // Copy the characters #else - strncpy(d,s,len); // Copy the characters + strncpy(d, s, len); // Copy the characters #endif d[len] = '\0'; - return d; // Return the new string + return d; // Return the new string } -char* readline(const char* p) -{ +char *readline(const char *p) { std::string retval; - std::cout << p ; + std::cout << p; std::getline(std::cin, retval); return std::cin.eof() ? nullptr : mystrdup(retval.c_str()); } - -void add_history(const char* /*unused*/){} -void using_history(){} +void add_history(const char * /*unused*/) {} +void using_history() {} #endif - - -void *cast_module_symbol(std::vector (*t_path)()) -{ - union cast_union - { +void *cast_module_symbol(std::vector (*t_path)()) { + union cast_union { std::vector (*in_ptr)(); void *out_ptr; }; @@ -67,26 +61,23 @@ void *cast_module_symbol(std::vector (*t_path)()) return c.out_ptr; } -std::vector default_search_paths() -{ +std::vector default_search_paths() { std::vector paths; #ifndef CHAISCRIPT_NO_DYNLOAD -#ifdef CHAISCRIPT_WINDOWS // force no unicode +#ifdef CHAISCRIPT_WINDOWS // force no unicode CHAR path[4096]; - int size = GetModuleFileNameA(nullptr, path, sizeof(path)-1); + int size = GetModuleFileNameA(nullptr, path, sizeof(path) - 1); std::string exepath(path, size); size_t lastslash = exepath.rfind('\\'); size_t secondtolastslash = exepath.rfind('\\', lastslash - 1); - if (lastslash != std::string::npos) - { + if (lastslash != std::string::npos) { paths.push_back(exepath.substr(0, lastslash)); } - if (secondtolastslash != std::string::npos) - { + if (secondtolastslash != std::string::npos) { return {exepath.substr(0, secondtolastslash) + "\\lib\\chaiscript\\"}; } #else @@ -96,32 +87,26 @@ std::vector default_search_paths() std::vector buf(2048); ssize_t size = -1; - if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) - { + if ((size = readlink("/proc/self/exe", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } - if (exepath.empty()) - { - if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/curproc/file", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { - if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) - { + if (exepath.empty()) { + if ((size = readlink("/proc/self/path/a.out", &buf.front(), buf.size())) >= 0) { exepath = std::string(&buf.front(), static_cast(size)); } } - if (exepath.empty()) - { + if (exepath.empty()) { Dl_info rInfo; - memset( &rInfo, 0, sizeof(rInfo) ); - if ( dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr ) { + memset(&rInfo, 0, sizeof(rInfo)); + if (dladdr(cast_module_symbol(&default_search_paths), &rInfo) == 0 || rInfo.dli_fname == nullptr) { return paths; } @@ -131,13 +116,11 @@ std::vector default_search_paths() size_t lastslash = exepath.rfind('/'); size_t secondtolastslash = exepath.rfind('/', lastslash - 1); - if (lastslash != std::string::npos) - { - paths.push_back(exepath.substr(0, lastslash+1)); + if (lastslash != std::string::npos) { + paths.push_back(exepath.substr(0, lastslash + 1)); } - if (secondtolastslash != std::string::npos) - { + if (secondtolastslash != std::string::npos) { paths.push_back(exepath.substr(0, secondtolastslash) + "/lib/chaiscript/"); } #endif @@ -147,25 +130,24 @@ std::vector default_search_paths() } void help(int n) { - if ( n >= 0 ) { + if (n >= 0) { std::cout << "ChaiScript evaluator. To evaluate an expression, type it and press .\n"; std::cout << "Additionally, you can inspect the runtime system using:\n"; std::cout << " dump_system() - outputs all functions registered to the system\n"; std::cout << " dump_object(x) - dumps information about the given symbol\n"; } else { std::cout << "usage : chai [option]+\n"; - std::cout << "option:" << '\n'; - std::cout << " -h | --help" << '\n'; - std::cout << " -i | --interactive" << '\n'; - std::cout << " -c | --command cmd" << '\n'; - std::cout << " -v | --version" << '\n'; - std::cout << " - --stdin" << '\n'; - std::cout << " filepath" << '\n'; + std::cout << "option:" << '\n'; + std::cout << " -h | --help" << '\n'; + std::cout << " -i | --interactive" << '\n'; + std::cout << " -c | --command cmd" << '\n'; + std::cout << " -v | --version" << '\n'; + std::cout << " - --stdin" << '\n'; + std::cout << " filepath" << '\n'; } } -std::string throws_exception(const std::function &f) -{ +std::string throws_exception(const std::function &f) { try { f(); } catch (const std::exception &e) { @@ -175,8 +157,7 @@ std::string throws_exception(const std::function &f) return ""; } -chaiscript::exception::eval_error get_eval_error(const std::function &f) -{ +chaiscript::exception::eval_error get_eval_error(const std::function &f) { try { f(); } catch (const chaiscript::exception::eval_error &e) { @@ -188,21 +169,19 @@ chaiscript::exception::eval_error get_eval_error(const std::function &f std::string get_next_command() { std::string retval("quit"); - if ( ! std::cin.eof() ) { + if (!std::cin.eof()) { char *input_raw = readline("eval> "); - if ( input_raw != nullptr ) { + if (input_raw != nullptr) { add_history(input_raw); std::string val(input_raw); size_t pos = val.find_first_not_of("\t \n"); - if (pos != std::string::npos) - { + if (pos != std::string::npos) { val.erase(0, pos); } pos = val.find_last_not_of("\t \n"); - if (pos != std::string::npos) - { - val.erase(pos+1, std::string::npos); + if (pos != std::string::npos) { + val.erase(pos + 1, std::string::npos); } retval = val; @@ -210,11 +189,7 @@ std::string get_next_command() { ::free(input_raw); } } - if( retval == "quit" - || retval == "exit" - || retval == "help" - || retval == "version") - { + if (retval == "quit" || retval == "exit" || retval == "help" || retval == "version") { retval += "(0)"; } return retval; @@ -226,8 +201,7 @@ void myexit(int return_val) { exit(return_val); } -void interactive(chaiscript::ChaiScript_Basic& chai) -{ +void interactive(chaiscript::ChaiScript_Basic &chai) { using_history(); for (;;) { @@ -236,39 +210,34 @@ void interactive(chaiscript::ChaiScript_Basic& chai) // evaluate input chaiscript::Boxed_Value val = chai.eval(input); - //Then, we try to print the result of the evaluation to the user + // Then, we try to print the result of the evaluation to the user if (!val.get_type_info().bare_equal(chaiscript::user_type())) { try { - std::cout << chai.eval >("to_string")(val) << '\n'; - } - catch (...) {} //If we can't, do nothing + std::cout << chai.eval>("to_string")(val) << '\n'; + } catch (...) { + } // If we can't, do nothing } - } - catch (const chaiscript::exception::eval_error &ee) { + } catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.what(); - if ( !ee.call_stack.empty() ) { + if (!ee.call_stack.empty()) { std::cout << "during evaluation at (" << ee.call_stack[0].start().line << ", " << ee.call_stack[0].start().column << ")"; } std::cout << '\n'; - } - catch (const std::exception &e) { + } catch (const std::exception &e) { std::cout << e.what(); std::cout << '\n'; } } } -double now() -{ +double now() { using namespace std::chrono; auto now = high_resolution_clock::now(); return duration_cast>(now.time_since_epoch()).count(); } -int main(int argc, char *argv[]) -{ - - // Disable deprecation warning for getenv call. +int main(int argc, char *argv[]) { +// Disable deprecation warning for getenv call. #ifdef CHAISCRIPT_MSVC #pragma warning(push) #pragma warning(disable : 4996) @@ -282,22 +251,20 @@ int main(int argc, char *argv[]) #endif std::vector usepaths; - usepaths.push_back(""); - if (usepath != nullptr) - { - usepaths.push_back(usepath); + usepaths.emplace_back(""); + if (usepath != nullptr) { + usepaths.emplace_back(usepath); } std::vector modulepaths; std::vector searchpaths = default_search_paths(); modulepaths.insert(modulepaths.end(), searchpaths.begin(), searchpaths.end()); - modulepaths.push_back(""); - if (modulepath != nullptr) - { - modulepaths.push_back(modulepath); + modulepaths.emplace_back(""); + if (modulepath != nullptr) { + modulepaths.emplace_back(modulepath); } - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(),modulepaths,usepaths); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser(), modulepaths, usepaths); chai.add(chaiscript::fun(&myexit), "exit"); chai.add(chaiscript::fun(&myexit), "quit"); @@ -311,46 +278,48 @@ int main(int argc, char *argv[]) bool any_exception_ok = false; for (int i = 0; i < argc; ++i) { - if ( i == 0 && argc > 1 ) { + if (i == 0 && argc > 1) { ++i; } - std::string arg( i != 0 ? argv[i] : "--interactive" ); + std::string arg(i != 0 ? argv[i] : "--interactive"); - enum { eInteractive - , eCommand - , eFile - } mode = eCommand ; + enum { + eInteractive, + eCommand, + eFile + } mode + = eCommand; - if ( arg == "-c" || arg == "--command" ) { - if ( (i+1) >= argc ) { + if (arg == "-c" || arg == "--command") { + if ((i + 1) >= argc) { std::cout << "insufficient input following " << arg << '\n'; return EXIT_FAILURE; - } - arg = argv[++i]; - - } else if ( arg == "-" || arg == "--stdin" ) { - arg = "" ; - std::string line; - while ( std::getline(std::cin, line) ) { - arg += line + '\n' ; } - } else if ( arg == "-v" || arg == "--version" ) { - arg = "print(version())" ; - } else if ( arg == "-h" || arg == "--help" ) { + arg = argv[++i]; + + } else if (arg == "-" || arg == "--stdin") { + arg = ""; + std::string line; + while (std::getline(std::cin, line)) { + arg += line + '\n'; + } + } else if (arg == "-v" || arg == "--version") { + arg = "print(version())"; + } else if (arg == "-h" || arg == "--help") { arg = "help(-1)"; - } else if ( arg == "-e" || arg == "--evalerrorok" ) { + } else if (arg == "-e" || arg == "--evalerrorok") { eval_error_ok = true; continue; - } else if ( arg == "--exception" ) { + } else if (arg == "--exception") { boxed_exception_ok = true; continue; - } else if ( arg == "--any-exception" ) { + } else if (arg == "--any-exception") { any_exception_ok = true; continue; - } else if ( arg == "-i" || arg == "--interactive" ) { - mode = eInteractive ; - } else if ( arg.find('-') == 0 ) { + } else if (arg == "-i" || arg == "--interactive") { + mode = eInteractive; + } else if (arg.find('-') == 0) { std::cout << "unrecognised argument " << arg << '\n'; return EXIT_FAILURE; } else { @@ -358,7 +327,7 @@ int main(int argc, char *argv[]) } try { - switch ( mode ) { + switch (mode) { case eInteractive: interactive(chai); break; @@ -368,32 +337,28 @@ int main(int argc, char *argv[]) case eFile: chai.eval_file(arg); } - } - catch (const chaiscript::exception::eval_error &ee) { + } catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.pretty_print(); std::cout << '\n'; if (!eval_error_ok) { return EXIT_FAILURE; } - } - catch (const chaiscript::Boxed_Value &e) { + } catch (const chaiscript::Boxed_Value &e) { std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n'; if (!boxed_exception_ok) { return EXIT_FAILURE; } - } - catch (const chaiscript::exception::load_module_error &e) { - std::cout << "Unhandled module load error\n" << e.what() << '\n'; - } - catch (std::exception &e) { + } catch (const chaiscript::exception::load_module_error &e) { + std::cout << "Unhandled module load error\n" + << e.what() << '\n'; + } catch (std::exception &e) { std::cout << "Unhandled standard exception: " << e.what() << '\n'; if (!any_exception_ok) { throw; } - } - catch (...) { + } catch (...) { std::cout << "Unhandled unknown exception" << '\n'; if (!any_exception_ok) { throw; diff --git a/samples/example.cpp b/src/sanity_checks.cpp similarity index 63% rename from samples/example.cpp rename to src/sanity_checks.cpp index 4810a127..9d87b5d9 100644 --- a/samples/example.cpp +++ b/src/sanity_checks.cpp @@ -4,62 +4,52 @@ // and Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com -#include +// NOT TO BE USED AS A SOURCE OF BEST PRACTICES +// FOR CHAISCRIPT + #include +#include #include #include #include -void log(const std::string &msg) -{ +void log(const std::string &msg) { std::cout << "[" << time(nullptr) << "] " << msg << '\n'; } -void log(const std::string &module, const std::string &msg) -{ +void log(const std::string &module, const std::string &msg) { std::cout << "[" << time(nullptr) << "] <" << module << "> " << msg << '\n'; } -void bound_log(const std::string &msg) -{ +void bound_log(const std::string &msg) { log(msg); } -void hello_world(const chaiscript::Boxed_Value & /*o*/) -{ +void hello_world(const chaiscript::Boxed_Value & /*o*/) { std::cout << "Hello World\n"; } -void hello_constructor(const chaiscript::Boxed_Value & /*o*/) -{ +void hello_constructor(const chaiscript::Boxed_Value & /*o*/) { std::cout << "Hello Constructor\n"; } +struct System { + std::map> m_callbacks; -struct System -{ - std::map > m_callbacks; - - void add_callback(const std::string &t_name, - const std::function &t_func) - { + void add_callback(const std::string &t_name, const std::function &t_func) { m_callbacks[t_name] = t_func; } - - void do_callbacks(const std::string &inp) - { + void do_callbacks(const std::string &inp) { log("Running Callbacks: " + inp); - for (auto & m_callback : m_callbacks) - { + for (auto &m_callback : m_callbacks) { log("Callback: " + m_callback.first, m_callback.second(inp)); } } }; -void take_shared_ptr(const std::shared_ptr &p) -{ +void take_shared_ptr(const std::shared_ptr &p) { std::cout << *p << '\n'; } @@ -67,27 +57,26 @@ int main(int /*argc*/, char * /*argv*/[]) { using namespace chaiscript; ChaiScript chai; - - //Create a new system object and share it with the chaiscript engine + // Create a new system object and share it with the chaiscript engine System system; chai.add_global(var(&system), "system"); - //Add a bound callback method + // Add a bound callback method chai.add(fun(&System::add_callback, std::ref(system)), "add_callback_bound"); - //Register the two methods of the System structure. + // Register the two methods of the System structure. chai.add(fun(&System::add_callback), "add_callback"); chai.add(fun(&System::do_callbacks), "do_callbacks"); chai.add(fun(&take_shared_ptr), "take_shared_ptr"); - // Let's use chaiscript to add a new lambda callback to our system. + // Let's use chaiscript to add a new lambda callback to our system. // The function "{ 'Callback1' + x }" is created in chaiscript and passed into our C++ application - // in the "add_callback" function of struct System the chaiscript function is converted into a + // in the "add_callback" function of struct System the chaiscript function is converted into a // std::function, so it can be handled and called easily and type-safely chai.eval(R"(system.add_callback("#1", fun(x) { "Callback1 " + x });)"); - + // Because we are sharing the "system" object with the chaiscript engine we have equal // access to it both from within chaiscript and from C++ code system.do_callbacks("TestString"); @@ -106,29 +95,26 @@ int main(int /*argc*/, char * /*argv*/[]) { // A shortcut to using eval is just to use the chai operator() chai(R"(log("Test Module", "Test Message");)"); - //Finally, it is possible to register a lambda as a system function, in this - //way, we can, for instance add a bound member function to the system - chai.add(fun([&system](){ return system.do_callbacks("Bound Test"); }), "do_callbacks"); + // Finally, it is possible to register a lambda as a system function, in this + // way, we can, for instance add a bound member function to the system + chai.add(fun([&system]() { return system.do_callbacks("Bound Test"); }), "do_callbacks"); - //Call bound version of do_callbacks + // Call bound version of do_callbacks chai("do_callbacks()"); - std::function caller = chai.eval >( - R"(fun() { system.do_callbacks("From Functor"); })" - ); + std::function caller = chai.eval>(R"(fun() { system.do_callbacks("From Functor"); })"); caller(); - - //If we would like a type-safe return value from all call, we can use - //the templated version of eval: + // If we would like a type-safe return value from all call, we can use + // the templated version of eval: int i = chai.eval("5+5"); std::cout << "5+5: " << i << '\n'; - //Add a new variable + // Add a new variable chai("var scripti = 15"); - //We can even get a handle to the variables in the system + // We can even get a handle to the variables in the system int &scripti = chai.eval("scripti"); std::cout << "scripti: " << scripti << '\n'; @@ -136,10 +122,10 @@ int main(int /*argc*/, char * /*argv*/[]) { std::cout << "scripti (updated): " << scripti << '\n'; chai(R"(print("Scripti from chai: " + to_string(scripti)))"); - //To do: Add examples of handling Boxed_Values directly when needed + // To do: Add examples of handling Boxed_Values directly when needed - //Creating a functor on the stack and using it immediately - int x = chai.eval >("fun (x, y) { return x + y; }")(5, 6); + // Creating a functor on the stack and using it immediately + int x = chai.eval>("fun (x, y) { return x + y; }")(5, 6); std::stringstream ss; ss << x; @@ -153,25 +139,23 @@ int main(int /*argc*/, char * /*argv*/[]) { chai("def getvar() { return constvar; }"); chai("print( getvar() )"); - - //Ability to create our own container types when needed. std::vector and std::map are - //mostly supported currently - chai.add(bootstrap::standard_library::vector_type >("IntVector")); - + // Ability to create our own container types when needed. std::vector and std::map are + // mostly supported currently + // chai.add(bootstrap::standard_library::vector_type >("IntVector")); // Test ability to register a function that excepts a shared_ptr version of a type chai(R"(take_shared_ptr("Hello World as a shared_ptr");)"); chai.add(fun(&bound_log, std::string("Msg")), "BoundFun"); - //Dynamic objects test + // Dynamic objects test chai.add(chaiscript::Proxy_Function(new dispatch::detail::Dynamic_Object_Function("TestType", fun(&hello_world))), "hello_world"); chai.add(chaiscript::Proxy_Function(new dispatch::detail::Dynamic_Object_Constructor("TestType", fun(&hello_constructor))), "TestType"); -// chai.add(fun(std::function(std::bind(&dispatch::detail::Dynamic_Object_Attribute::func, "TestType", "attr", std::placeholders::_1))), "attr"); + // chai.add(fun(std::function(std::bind(&dispatch::detail::Dynamic_Object_Attribute::func, + // "TestType", "attr", std::placeholders::_1))), "attr"); chai.eval("var x = TestType()"); -// chai.eval("x.attr = \"hi\""); -// chai.eval("print(x.attr)"); + // chai.eval("x.attr = \"hi\""); + // chai.eval("print(x.attr)"); chai.eval("x.hello_world()"); } - diff --git a/src/sha3.cpp b/src/sha3.cpp index f962512e..ea16f57b 100644 --- a/src/sha3.cpp +++ b/src/sha3.cpp @@ -11,52 +11,35 @@ #include #endif - /// same as reset() SHA3::SHA3(Bits bits) -: m_blockSize(200 - 2 * (bits / 8)), - m_bits(bits) -{ + : m_blockSize(200 - 2 * (bits / 8)) + , m_bits(bits) { reset(); } - /// restart -void SHA3::reset() -{ +void SHA3::reset() { for (size_t i = 0; i < StateSize; i++) m_hash[i] = 0; - m_numBytes = 0; + m_numBytes = 0; m_bufferSize = 0; } - /// constants and local helper functions -namespace -{ +namespace { const unsigned int Rounds = 24; - const uint64_t XorMasks[Rounds] = - { - 0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, - 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, - 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, - 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, - 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, - 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, - 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, - 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL - }; + const uint64_t XorMasks[Rounds] + = {0x0000000000000001ULL, 0x0000000000008082ULL, 0x800000000000808aULL, 0x8000000080008000ULL, 0x000000000000808bULL, 0x0000000080000001ULL, 0x8000000080008081ULL, 0x8000000000008009ULL, 0x000000000000008aULL, 0x0000000000000088ULL, 0x0000000080008009ULL, 0x000000008000000aULL, 0x000000008000808bULL, 0x800000000000008bULL, 0x8000000000008089ULL, 0x8000000000008003ULL, 0x8000000000008002ULL, 0x8000000000000080ULL, 0x000000000000800aULL, 0x800000008000000aULL, 0x8000000080008081ULL, 0x8000000000008080ULL, 0x0000000080000001ULL, 0x8000000080008008ULL}; /// rotate left and wrap around to the right - inline uint64_t rotateLeft(uint64_t x, uint8_t numBits) - { + inline uint64_t rotateLeft(uint64_t x, uint8_t numBits) { return (x << numBits) | (x >> (64 - numBits)); } /// convert litte vs big endian - inline uint64_t swap(uint64_t x) - { + inline uint64_t swap(uint64_t x) { #if defined(__GNUC__) || defined(__clang__) return __builtin_bswap64(x); #endif @@ -64,55 +47,43 @@ namespace return _byteswap_uint64(x); #endif - return (x >> 56) | - ((x >> 40) & 0x000000000000FF00ULL) | - ((x >> 24) & 0x0000000000FF0000ULL) | - ((x >> 8) & 0x00000000FF000000ULL) | - ((x << 8) & 0x000000FF00000000ULL) | - ((x << 24) & 0x0000FF0000000000ULL) | - ((x << 40) & 0x00FF000000000000ULL) | - (x << 56); + return (x >> 56) | ((x >> 40) & 0x000000000000FF00ULL) | ((x >> 24) & 0x0000000000FF0000ULL) | ((x >> 8) & 0x00000000FF000000ULL) + | ((x << 8) & 0x000000FF00000000ULL) | ((x << 24) & 0x0000FF0000000000ULL) | ((x << 40) & 0x00FF000000000000ULL) | (x << 56); } - /// return x % 5 for 0 <= x <= 9 - unsigned int mod5(unsigned int x) - { + unsigned int mod5(unsigned int x) { if (x < 5) return x; return x - 5; } -} - +} // namespace /// process a full block -void SHA3::processBlock(const void* data) -{ +void SHA3::processBlock(const void *data) { #if defined(__BYTE_ORDER) && (__BYTE_ORDER != 0) && (__BYTE_ORDER == __BIG_ENDIAN) #define LITTLEENDIAN(x) swap(x) #else #define LITTLEENDIAN(x) (x) #endif - const uint64_t* data64 = (const uint64_t*) data; + const uint64_t *data64 = (const uint64_t *)data; // mix data into state for (unsigned int i = 0; i < m_blockSize / 8; i++) m_hash[i] ^= LITTLEENDIAN(data64[i]); // re-compute state - for (unsigned int round = 0; round < Rounds; round++) - { + for (unsigned int round = 0; round < Rounds; round++) { // Theta uint64_t coefficients[5]; for (unsigned int i = 0; i < 5; i++) coefficients[i] = m_hash[i] ^ m_hash[i + 5] ^ m_hash[i + 10] ^ m_hash[i + 15] ^ m_hash[i + 20]; - for (unsigned int i = 0; i < 5; i++) - { + for (unsigned int i = 0; i < 5; i++) { uint64_t one = coefficients[mod5(i + 4)] ^ rotateLeft(coefficients[mod5(i + 1)], 1); - m_hash[i ] ^= one; - m_hash[i + 5] ^= one; + m_hash[i] ^= one; + m_hash[i + 5] ^= one; m_hash[i + 10] ^= one; m_hash[i + 15] ^= one; m_hash[i + 20] ^= one; @@ -123,43 +94,88 @@ void SHA3::processBlock(const void* data) // Rho Pi uint64_t last = m_hash[1]; - one = m_hash[10]; m_hash[10] = rotateLeft(last, 1); last = one; - one = m_hash[ 7]; m_hash[ 7] = rotateLeft(last, 3); last = one; - one = m_hash[11]; m_hash[11] = rotateLeft(last, 6); last = one; - one = m_hash[17]; m_hash[17] = rotateLeft(last, 10); last = one; - one = m_hash[18]; m_hash[18] = rotateLeft(last, 15); last = one; - one = m_hash[ 3]; m_hash[ 3] = rotateLeft(last, 21); last = one; - one = m_hash[ 5]; m_hash[ 5] = rotateLeft(last, 28); last = one; - one = m_hash[16]; m_hash[16] = rotateLeft(last, 36); last = one; - one = m_hash[ 8]; m_hash[ 8] = rotateLeft(last, 45); last = one; - one = m_hash[21]; m_hash[21] = rotateLeft(last, 55); last = one; - one = m_hash[24]; m_hash[24] = rotateLeft(last, 2); last = one; - one = m_hash[ 4]; m_hash[ 4] = rotateLeft(last, 14); last = one; - one = m_hash[15]; m_hash[15] = rotateLeft(last, 27); last = one; - one = m_hash[23]; m_hash[23] = rotateLeft(last, 41); last = one; - one = m_hash[19]; m_hash[19] = rotateLeft(last, 56); last = one; - one = m_hash[13]; m_hash[13] = rotateLeft(last, 8); last = one; - one = m_hash[12]; m_hash[12] = rotateLeft(last, 25); last = one; - one = m_hash[ 2]; m_hash[ 2] = rotateLeft(last, 43); last = one; - one = m_hash[20]; m_hash[20] = rotateLeft(last, 62); last = one; - one = m_hash[14]; m_hash[14] = rotateLeft(last, 18); last = one; - one = m_hash[22]; m_hash[22] = rotateLeft(last, 39); last = one; - one = m_hash[ 9]; m_hash[ 9] = rotateLeft(last, 61); last = one; - one = m_hash[ 6]; m_hash[ 6] = rotateLeft(last, 20); last = one; - m_hash[ 1] = rotateLeft(last, 44); + one = m_hash[10]; + m_hash[10] = rotateLeft(last, 1); + last = one; + one = m_hash[7]; + m_hash[7] = rotateLeft(last, 3); + last = one; + one = m_hash[11]; + m_hash[11] = rotateLeft(last, 6); + last = one; + one = m_hash[17]; + m_hash[17] = rotateLeft(last, 10); + last = one; + one = m_hash[18]; + m_hash[18] = rotateLeft(last, 15); + last = one; + one = m_hash[3]; + m_hash[3] = rotateLeft(last, 21); + last = one; + one = m_hash[5]; + m_hash[5] = rotateLeft(last, 28); + last = one; + one = m_hash[16]; + m_hash[16] = rotateLeft(last, 36); + last = one; + one = m_hash[8]; + m_hash[8] = rotateLeft(last, 45); + last = one; + one = m_hash[21]; + m_hash[21] = rotateLeft(last, 55); + last = one; + one = m_hash[24]; + m_hash[24] = rotateLeft(last, 2); + last = one; + one = m_hash[4]; + m_hash[4] = rotateLeft(last, 14); + last = one; + one = m_hash[15]; + m_hash[15] = rotateLeft(last, 27); + last = one; + one = m_hash[23]; + m_hash[23] = rotateLeft(last, 41); + last = one; + one = m_hash[19]; + m_hash[19] = rotateLeft(last, 56); + last = one; + one = m_hash[13]; + m_hash[13] = rotateLeft(last, 8); + last = one; + one = m_hash[12]; + m_hash[12] = rotateLeft(last, 25); + last = one; + one = m_hash[2]; + m_hash[2] = rotateLeft(last, 43); + last = one; + one = m_hash[20]; + m_hash[20] = rotateLeft(last, 62); + last = one; + one = m_hash[14]; + m_hash[14] = rotateLeft(last, 18); + last = one; + one = m_hash[22]; + m_hash[22] = rotateLeft(last, 39); + last = one; + one = m_hash[9]; + m_hash[9] = rotateLeft(last, 61); + last = one; + one = m_hash[6]; + m_hash[6] = rotateLeft(last, 20); + last = one; + m_hash[1] = rotateLeft(last, 44); // Chi - for (unsigned int j = 0; j < 25; j += 5) - { + for (unsigned int j = 0; j < 25; j += 5) { // temporaries uint64_t one = m_hash[j]; uint64_t two = m_hash[j + 1]; - m_hash[j] ^= m_hash[j + 2] & ~two; + m_hash[j] ^= m_hash[j + 2] & ~two; m_hash[j + 1] ^= m_hash[j + 3] & ~m_hash[j + 2]; m_hash[j + 2] ^= m_hash[j + 4] & ~m_hash[j + 3]; - m_hash[j + 3] ^= one & ~m_hash[j + 4]; - m_hash[j + 4] ^= two & ~one; + m_hash[j + 3] ^= one & ~m_hash[j + 4]; + m_hash[j + 4] ^= two & ~one; } // Iota @@ -167,27 +183,22 @@ void SHA3::processBlock(const void* data) } } - /// add arbitrary number of bytes -void SHA3::add(const void* data, size_t numBytes) -{ - const uint8_t* current = (const uint8_t*) data; +void SHA3::add(const void *data, size_t numBytes) { + const uint8_t *current = (const uint8_t *)data; // copy data to buffer - if (m_bufferSize > 0) - { - while (numBytes > 0 && m_bufferSize < m_blockSize) - { + if (m_bufferSize > 0) { + while (numBytes > 0 && m_bufferSize < m_blockSize) { m_buffer[m_bufferSize++] = *current++; numBytes--; } } // full buffer - if (m_bufferSize == m_blockSize) - { - processBlock((void*)m_buffer); - m_numBytes += m_blockSize; + if (m_bufferSize == m_blockSize) { + processBlock((void *)m_buffer); + m_numBytes += m_blockSize; m_bufferSize = 0; } @@ -196,26 +207,22 @@ void SHA3::add(const void* data, size_t numBytes) return; // process full blocks - while (numBytes >= m_blockSize) - { + while (numBytes >= m_blockSize) { processBlock(current); - current += m_blockSize; + current += m_blockSize; m_numBytes += m_blockSize; - numBytes -= m_blockSize; + numBytes -= m_blockSize; } // keep remaining bytes in buffer - while (numBytes > 0) - { + while (numBytes > 0) { m_buffer[m_bufferSize++] = *current++; numBytes--; } } - /// process everything left in the internal buffer -void SHA3::processBuffer() -{ +void SHA3::processBuffer() { // add padding size_t offset = m_bufferSize; // add a "1" byte @@ -230,10 +237,8 @@ void SHA3::processBuffer() processBlock(m_buffer); } - /// return latest hash as 16 hex characters -std::string SHA3::getHash() -{ +std::string SHA3::getHash() { // process remaining bytes processBuffer(); @@ -249,7 +254,7 @@ std::string SHA3::getHash() for (unsigned int j = 0; j < 8; j++) // 64 bits => 8 bytes { // convert a byte to hex - unsigned char oneByte = (unsigned char) (m_hash[i] >> (8 * j)); + unsigned char oneByte = (unsigned char)(m_hash[i] >> (8 * j)); result += dec2hex[oneByte >> 4]; result += dec2hex[oneByte & 15]; } @@ -257,10 +262,9 @@ std::string SHA3::getHash() // SHA3-224's last entry in m_hash provides only 32 bits instead of 64 bits unsigned int remainder = m_bits - hashLength * 64; unsigned int processed = 0; - while (processed < remainder) - { + while (processed < remainder) { // convert a byte to hex - unsigned char oneByte = (unsigned char) (m_hash[hashLength] >> processed); + unsigned char oneByte = (unsigned char)(m_hash[hashLength] >> processed); result += dec2hex[oneByte >> 4]; result += dec2hex[oneByte & 15]; @@ -270,19 +274,15 @@ std::string SHA3::getHash() return result; } - /// compute SHA3 of a memory block -std::string SHA3::operator()(const void* data, size_t numBytes) -{ +std::string SHA3::operator()(const void *data, size_t numBytes) { reset(); add(data, numBytes); return getHash(); } - /// compute SHA3 of a string, excluding final zero -std::string SHA3::operator()(const std::string& text) -{ +std::string SHA3::operator()(const std::string &text) { reset(); add(text.c_str(), text.size()); return getHash(); diff --git a/src/stl_extra.cpp b/src/stl_extra.cpp index cd0ec663..0f456b09 100644 --- a/src/stl_extra.cpp +++ b/src/stl_extra.cpp @@ -1,13 +1,12 @@ // This file is distributed under the BSD License. // See "license.txt" for details. // Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com) -// Copyright 2009-2017, Jason Turner (jason@emptycrate.com) +// Copyright 2009-2018, Jason Turner (jason@emptycrate.com) // http://www.chaiscript.com // This is an open source non-commercial project. Dear PVS-Studio, please check it. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include #include #include @@ -25,11 +24,10 @@ #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" #endif -CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_stl_extra() -{ +CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_stl_extra() { auto module = std::make_shared(); - chaiscript::bootstrap::standard_library::list_type >("List", *module); - chaiscript::bootstrap::standard_library::vector_type >("u16vector", *module); + chaiscript::bootstrap::standard_library::list_type>("List", *module); + chaiscript::bootstrap::standard_library::vector_type>("u16vector", *module); module->add(chaiscript::vector_conversion>()); return module; } diff --git a/src/test_module.cpp b/src/test_module.cpp index 90a1154a..6568b9b2 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -3,133 +3,121 @@ #include #include +class TestBaseType { +public: + TestBaseType() + : val(10) + , const_val(15) + , mdarray{} { + } + TestBaseType(int) + : val(10) + , const_val(15) + , mdarray{} { + } + TestBaseType(int *) + : val(10) + , const_val(15) + , mdarray{} { + } + TestBaseType(const TestBaseType &other) + : val(other.val) + , const_val(other.const_val) + , const_val_ptr(&const_val) + , func_member(other.func_member) { + } -class TestBaseType -{ - public: - TestBaseType() : val(10), const_val(15), mdarray{} { } - TestBaseType(int) : val(10), const_val(15), mdarray{} { } - TestBaseType(int *) : val(10), const_val(15), mdarray{} { } + TestBaseType(TestBaseType &&other) + : val(other.val) + , const_val(other.const_val) + , const_val_ptr(&const_val) + , func_member(std::move(other.func_member)) { + } - TestBaseType(const TestBaseType &) = default; - virtual ~TestBaseType() {} - virtual int func() { return 0; } + TestBaseType &operator=(TestBaseType &&) = delete; + TestBaseType &operator=(const TestBaseType &) = delete; - int base_only_func() { return -9; } + virtual ~TestBaseType() = default; + virtual int func() { return 0; } - const TestBaseType &constMe() const { return *this; } + int base_only_func() { return -9; } - int val; - const int const_val; - const int *const_val_ptr = &const_val; + const TestBaseType &constMe() const { return *this; } - const int *get_const_val_ptr() { - return const_val_ptr; - } + int val; + const int const_val; + const int *const_val_ptr = &const_val; - int mdarray[2][3][5]; - std::function func_member; + const int *get_const_val_ptr() { return const_val_ptr; } - void set_string_val(std::string &t_str) - { - t_str = "42"; - } + int mdarray[2][3][5]; + std::function func_member; - private: - TestBaseType &operator=(const TestBaseType &) = delete; + void set_string_val(std::string &t_str) { t_str = "42"; } }; -class Type2 -{ - public: - Type2(TestBaseType t_bt) - : m_bt(std::move(t_bt)), - m_str("Hello World") - { - } +class Type2 { +public: + Type2(TestBaseType t_bt) + : m_bt(std::move(t_bt)) + , m_str("Hello World") { + } - int get_val() const - { - return m_bt.val; - } + int get_val() const { return m_bt.val; } + const char *get_str() const { return m_str.c_str(); } - const char *get_str() const - { - return m_str.c_str(); - } - - private: - TestBaseType m_bt; - std::string m_str; +private: + TestBaseType m_bt; + std::string m_str; }; -enum TestEnum -{ +enum TestEnum { TestValue1 = 1 }; -int to_int(TestEnum t) -{ +int to_int(TestEnum t) { return t; } -class TestDerivedType : public TestBaseType -{ - public: - ~TestDerivedType() override {} - TestDerivedType(const TestDerivedType &) = default; - TestDerivedType() = default; - virtual int func() override { return 1; } - int derived_only_func() { return 19; } - - private: - TestDerivedType &operator=(const TestDerivedType &) = delete; +class TestDerivedType : public TestBaseType { +public: + int func() override { return 1; } + int derived_only_func() { return 19; } }; -class TestMoreDerivedType : public TestDerivedType -{ - public: - TestMoreDerivedType(const TestMoreDerivedType &) = default; - TestMoreDerivedType() = default; - virtual ~TestMoreDerivedType() {} +class TestMoreDerivedType : public TestDerivedType { +public: }; -std::shared_ptr derived_type_factory() -{ +std::shared_ptr derived_type_factory() { return std::make_shared(); } -std::shared_ptr more_derived_type_factory() -{ +std::shared_ptr more_derived_type_factory() { return std::make_shared(); } -std::shared_ptr null_factory() -{ +std::shared_ptr null_factory() { return std::shared_ptr(); } -void update_shared_ptr(std::shared_ptr &ptr) -{ +void update_shared_ptr(std::shared_ptr &ptr) { ptr = std::make_shared(); } -void nullify_shared_ptr(std::shared_ptr &ptr) -{ +void nullify_shared_ptr(std::shared_ptr &ptr) { ptr = nullptr; } -std::string hello_world() -{ +std::string hello_world() { return "Hello World"; } static int global_i = 1; -int *get_new_int() -{ +int *get_new_int() { return &global_i; } @@ -145,8 +133,7 @@ int *get_new_int() #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" #endif -CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_module() -{ +CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_module() { chaiscript::ModulePtr m(new chaiscript::Module()); m->add(chaiscript::fun(hello_world), "hello_world"); @@ -156,16 +143,16 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::user_type(), "TestMoreDerivedType"); m->add(chaiscript::user_type(), "Type2"); - m->add(chaiscript::constructor(), "TestBaseType"); -// m->add(chaiscript::constructor(), "TestBaseType"); - m->add(chaiscript::constructor(), "TestBaseType"); - m->add(chaiscript::constructor(), "TestBaseType"); + m->add(chaiscript::constructor(), "TestBaseType"); + // m->add(chaiscript::constructor(), "TestBaseType"); + m->add(chaiscript::constructor(), "TestBaseType"); + m->add(chaiscript::constructor(), "TestBaseType"); - m->add(chaiscript::constructor(), "TestDerivedType"); - m->add(chaiscript::constructor(), "TestDerivedType"); + m->add(chaiscript::constructor(), "TestDerivedType"); + m->add(chaiscript::constructor(), "TestDerivedType"); - m->add(chaiscript::constructor(), "TestMoreDerivedType"); - m->add(chaiscript::constructor(), "TestMoreDerivedType"); + m->add(chaiscript::constructor(), "TestMoreDerivedType"); + m->add(chaiscript::constructor(), "TestMoreDerivedType"); /// \todo automatic chaining of base classes? m->add(chaiscript::base_class()); @@ -197,7 +184,6 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::fun(&TestBaseType::func_member), "func_member"); m->add(chaiscript::fun(&get_new_int), "get_new_int"); - m->add_global_const(chaiscript::const_var(TestValue1), "TestValue1"); m->add(chaiscript::user_type(), "TestEnum"); @@ -210,16 +196,14 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::fun(&Type2::get_val), "get_val"); m->add(chaiscript::fun(&Type2::get_str), "get_str"); m->add(chaiscript::type_conversion()); - m->add(chaiscript::constructor(), "Type2"); + m->add(chaiscript::constructor(), "Type2"); m->add(chaiscript::fun(&update_shared_ptr), "update_shared_ptr"); m->add(chaiscript::fun(&nullify_shared_ptr), "nullify_shared_ptr"); - return m; } - #ifdef __llvm__ #pragma clang diagnostic pop #endif diff --git a/static_libs/chaiscript_parser.cpp b/static_libs/chaiscript_parser.cpp index 15199bf3..e8a780a6 100644 --- a/static_libs/chaiscript_parser.cpp +++ b/static_libs/chaiscript_parser.cpp @@ -1,8 +1,6 @@ #include "../include/chaiscript/language/chaiscript_parser.hpp" #include "chaiscript_parser.hpp" -std::unique_ptr create_chaiscript_parser() -{ +std::unique_ptr create_chaiscript_parser() { return std::make_unique>(); } - diff --git a/static_libs/chaiscript_parser.hpp b/static_libs/chaiscript_parser.hpp index 6086749d..c463ae29 100644 --- a/static_libs/chaiscript_parser.hpp +++ b/static_libs/chaiscript_parser.hpp @@ -2,11 +2,13 @@ #ifndef CHAISCRIPT_PARSER_LIB #define CHAISCRIPT_PARSER_LIB +#include + namespace chaiscript { namespace parser { class ChaiScript_Parser_Base; } -} +} // namespace chaiscript std::unique_ptr create_chaiscript_parser(); diff --git a/static_libs/chaiscript_stdlib.cpp b/static_libs/chaiscript_stdlib.cpp index c786ece8..8c99a768 100644 --- a/static_libs/chaiscript_stdlib.cpp +++ b/static_libs/chaiscript_stdlib.cpp @@ -1,8 +1,6 @@ #include "../include/chaiscript/chaiscript_stdlib.hpp" #include "chaiscript_stdlib.hpp" -std::shared_ptr create_chaiscript_stdlib() -{ +std::shared_ptr create_chaiscript_stdlib() { return chaiscript::Std_Lib::library(); } - diff --git a/unittests/3.x/assign_const.chai b/unittests/3.x/assign_const.chai deleted file mode 100644 index 9726ed50..00000000 --- a/unittests/3.x/assign_const.chai +++ /dev/null @@ -1,2 +0,0 @@ -assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 = 2 } ); -assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { 1 + 2 = 2 } ); diff --git a/unittests/3.x/bind.chai b/unittests/3.x/bind.chai deleted file mode 100644 index 3c72673f..00000000 --- a/unittests/3.x/bind.chai +++ /dev/null @@ -1,2 +0,0 @@ -var prod = bind(foldl, _, `*`, 1.0) -assert_equal(60, prod([3, 4, 5])) diff --git a/unittests/3.x/bind2.chai b/unittests/3.x/bind2.chai deleted file mode 100644 index 0b8ddde3..00000000 --- a/unittests/3.x/bind2.chai +++ /dev/null @@ -1,34 +0,0 @@ - -def add(x, y) -{ - return x + y; -} - -assert_equal(2, add.get_arity()); - -var b = bind(add, 2, _); - -assert_equal(1, b.get_arity()); - -var c = bind(b, 3); - -assert_equal(0, c.get_arity()); - -assert_equal(6, b(4)); -assert_equal(5, c()); - -def concat2(a,b,c,d) -{ - return to_string(a) + to_string(b) + to_string(c) + to_string(d); -} - -var d = bind(concat2, _, " Hello ", _, " World"); -assert_equal(2, d.get_arity()); - -assert_equal("1 Hello 3 World", d(1,3)); - -var e = bind(`<`, _, 5); -var types = e.get_param_types(); -assert_equal(2, types.size()); -assert_equal(true, types[0].bare_equal(bool_type)); - diff --git a/unittests/3.x/block_start.chai b/unittests/3.x/block_start.chai deleted file mode 100644 index 4830af1f..00000000 --- a/unittests/3.x/block_start.chai +++ /dev/null @@ -1 +0,0 @@ -{print("hello")} diff --git a/unittests/3.x/bool_not.chai b/unittests/3.x/bool_not.chai deleted file mode 100644 index fe4d0f77..00000000 --- a/unittests/3.x/bool_not.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(false, !true) diff --git a/unittests/3.x/break_while.chai b/unittests/3.x/break_while.chai deleted file mode 100644 index 5ff897c8..00000000 --- a/unittests/3.x/break_while.chai +++ /dev/null @@ -1,7 +0,0 @@ -var i = 0 -while (i < 10) { - if (++i == 5) { - break - } -} -assert_equal(5, i); diff --git a/unittests/3.x/char_init.chai b/unittests/3.x/char_init.chai deleted file mode 100644 index ce764774..00000000 --- a/unittests/3.x/char_init.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal("b", to_string('b')) diff --git a/unittests/3.x/classification.chai b/unittests/3.x/classification.chai deleted file mode 100644 index 8c2cca94..00000000 --- a/unittests/3.x/classification.chai +++ /dev/null @@ -1,7 +0,0 @@ -assert_equal(true, 1.is_var_const()); -assert_equal(false, 1.is_var_reference()); -assert_equal(true, 1.is_var_pointer()); -assert_equal(false, 1.is_var_null()); -assert_equal(false, 1.is_var_undef()); -var i; -assert_equal(true, i.is_var_undef()); diff --git a/unittests/3.x/collate.chai b/unittests/3.x/collate.chai deleted file mode 100644 index 12632e59..00000000 --- a/unittests/3.x/collate.chai +++ /dev/null @@ -1,3 +0,0 @@ -var v = collate(1, 2) -assert_equal(1, v[0]) -assert_equal(2, v[1]) diff --git a/unittests/3.x/compare_gt.chai b/unittests/3.x/compare_gt.chai deleted file mode 100644 index 9a6ea456..00000000 --- a/unittests/3.x/compare_gt.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(false, 1 > 2); diff --git a/unittests/3.x/compare_lt.chai b/unittests/3.x/compare_lt.chai deleted file mode 100644 index 60641103..00000000 --- a/unittests/3.x/compare_lt.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(true, 1 < 2) diff --git a/unittests/3.x/concat.chai b/unittests/3.x/concat.chai deleted file mode 100644 index 3d285a5b..00000000 --- a/unittests/3.x/concat.chai +++ /dev/null @@ -1,5 +0,0 @@ -var v = concat([1, 2], [3, 4]); - -assert_equal(4, v.size()); -assert_equal(1, v[0]); -assert_equal(4, v[3]); diff --git a/unittests/3.x/const_range_test.chai b/unittests/3.x/const_range_test.chai deleted file mode 100644 index 5ebb5808..00000000 --- a/unittests/3.x/const_range_test.chai +++ /dev/null @@ -1,4 +0,0 @@ -//If the following succeeds, the test passes - - -"Hello World".for_each(fun(x) { print(x) } ) diff --git a/unittests/3.x/convert_double_string.chai b/unittests/3.x/convert_double_string.chai deleted file mode 100644 index e12b90f2..00000000 --- a/unittests/3.x/convert_double_string.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal("3.5bob", 3.5.to_string() + "bob"); diff --git a/unittests/3.x/convert_int_string.chai b/unittests/3.x/convert_int_string.chai deleted file mode 100644 index 0fcda326..00000000 --- a/unittests/3.x/convert_int_string.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal("3bob", 3.to_string + "bob") diff --git a/unittests/3.x/convert_string_double.chai b/unittests/3.x/convert_string_double.chai deleted file mode 100644 index b7b0b6ef..00000000 --- a/unittests/3.x/convert_string_double.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(6.8, "3.5".to_double() + 3.3) diff --git a/unittests/3.x/convert_string_int.chai b/unittests/3.x/convert_string_int.chai deleted file mode 100644 index e62eec95..00000000 --- a/unittests/3.x/convert_string_int.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(8, "4".to_int() + 4) diff --git a/unittests/3.x/deep_array_lookup.chai b/unittests/3.x/deep_array_lookup.chai deleted file mode 100644 index c405302d..00000000 --- a/unittests/3.x/deep_array_lookup.chai +++ /dev/null @@ -1,11 +0,0 @@ -var a = [1,2,3, [4,5,6] ] - -assert_equal(a[3][0], 4) - - -def Test::Test() { this.a = [1,2,3]; } -attr Test::a; - -var t = Test(); - -assert_equal(t.a[0], 1) diff --git a/unittests/3.x/dispatch_functions.chai b/unittests/3.x/dispatch_functions.chai deleted file mode 100644 index f25ae017..00000000 --- a/unittests/3.x/dispatch_functions.chai +++ /dev/null @@ -1,10 +0,0 @@ -assert_equal(`==`, `==`); -assert_not_equal(`==`, `<`); -assert_equal(`<`.get_arity(), 2); -assert_equal(get_arity.get_contained_functions().size(), 0); -assert_equal(get_arity.get_arity(), 1); -assert_equal(get_arity.get_param_types().size(), 2); - -var paramtypes = get_arity.get_param_types(); - -assert_equal(true, paramtypes[1].bare_equal(Function_type)); diff --git a/unittests/3.x/drop.chai b/unittests/3.x/drop.chai deleted file mode 100644 index c64b431e..00000000 --- a/unittests/3.x/drop.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([3,4], drop([1, 2, 3, 4], 2)) diff --git a/unittests/3.x/drop_while.chai b/unittests/3.x/drop_while.chai deleted file mode 100644 index 08e19f2f..00000000 --- a/unittests/3.x/drop_while.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([2, 3], drop_while([1, 2, 3], odd)) diff --git a/unittests/3.x/empty.chai b/unittests/3.x/empty.chai deleted file mode 100644 index e69de29b..00000000 diff --git a/unittests/3.x/equ_shortform.chai b/unittests/3.x/equ_shortform.chai deleted file mode 100644 index 41c8e1de..00000000 --- a/unittests/3.x/equ_shortform.chai +++ /dev/null @@ -1,4 +0,0 @@ -var x=.5 -assert_equal(.5, x) -var y=-.5 -assert_equal(-.5, y) diff --git a/unittests/3.x/eval.chai b/unittests/3.x/eval.chai deleted file mode 100644 index 2f18aa41..00000000 --- a/unittests/3.x/eval.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(7, eval("3 + 4")) diff --git a/unittests/3.x/eval_error.chai b/unittests/3.x/eval_error.chai deleted file mode 100644 index b5814450..00000000 --- a/unittests/3.x/eval_error.chai +++ /dev/null @@ -1,38 +0,0 @@ - -def deep() -{ - try { - } catch { - - } finally { - if (2) - { - } - - } -} - -def func() -{ - deep(); -} - -def doing() -{ - for (var i = 0; i < 10; ++i) - { - func(); - } -} - -def while_doing() -{ - while (true) - { - doing(); - } -} - -var f = fun() { while_doing(); } - -assert_true(get_eval_error(f).call_stack.size() <= 16) diff --git a/unittests/3.x/even.chai b/unittests/3.x/even.chai deleted file mode 100644 index 5a9a9aea..00000000 --- a/unittests/3.x/even.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(true, even(4)) diff --git a/unittests/3.x/exception.chai b/unittests/3.x/exception.chai deleted file mode 100644 index 50df6a80..00000000 --- a/unittests/3.x/exception.chai +++ /dev/null @@ -1,9 +0,0 @@ -var x = 1 -try { - throw(x) - x = 2 -} -catch(e) { - x = e + 3 -} -assert_equal(4, x); diff --git a/unittests/3.x/exception_finally.chai b/unittests/3.x/exception_finally.chai deleted file mode 100644 index d6fd834a..00000000 --- a/unittests/3.x/exception_finally.chai +++ /dev/null @@ -1,32 +0,0 @@ -var finallyone = false; - -try { - throw(3) -} -catch(x) { - assert_equal(3, x) -} -finally { - finallyone = true; -} - -assert_equal(true, finallyone); - -var try2 = false; -var catch2 = false; -var finally2 = false; - - -try { - try2 = true; -} -catch { - catch2 = true; -} -finally { - finally2 = true; -} - -assert_equal(true, try2); -assert_equal(false, catch2); -assert_equal(true, finally2); diff --git a/unittests/3.x/exception_guards.chai b/unittests/3.x/exception_guards.chai deleted file mode 100644 index 99cd9018..00000000 --- a/unittests/3.x/exception_guards.chai +++ /dev/null @@ -1,34 +0,0 @@ -var results = []; - -for (var i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results); diff --git a/unittests/3.x/filter.chai b/unittests/3.x/filter.chai deleted file mode 100644 index 6d805fee..00000000 --- a/unittests/3.x/filter.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([1,3], filter([1, 2, 3, 4], odd)) diff --git a/unittests/3.x/float.chai b/unittests/3.x/float.chai deleted file mode 100644 index b1bdf299..00000000 --- a/unittests/3.x/float.chai +++ /dev/null @@ -1,7 +0,0 @@ -assert_equal(true, 1.2 < 2) -assert_equal(true, 1.2 > 1) -assert_equal(1.2, 1.2) - -assert_equal(true, .5 > 0) -assert_equal(true, .5 < 1) -assert_equal(0.5, .5) diff --git a/unittests/3.x/foldl.chai b/unittests/3.x/foldl.chai deleted file mode 100644 index 7e9db51f..00000000 --- a/unittests/3.x/foldl.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(10, foldl([1, 2, 3, 4], `+`, 0)) diff --git a/unittests/3.x/for.chai b/unittests/3.x/for.chai deleted file mode 100644 index 9799be24..00000000 --- a/unittests/3.x/for.chai +++ /dev/null @@ -1,7 +0,0 @@ -var ret = [] - -for (var i = 0; i < 5; ++i) { - ret.push_back(i); -} - -assert_equal([0,1,2,3,4], ret); diff --git a/unittests/3.x/for_each.chai b/unittests/3.x/for_each.chai deleted file mode 100644 index 242a1baf..00000000 --- a/unittests/3.x/for_each.chai +++ /dev/null @@ -1 +0,0 @@ -for_each([1, 2, 3], print) diff --git a/unittests/3.x/for_each_range.chai b/unittests/3.x/for_each_range.chai deleted file mode 100644 index 43191bfb..00000000 --- a/unittests/3.x/for_each_range.chai +++ /dev/null @@ -1,3 +0,0 @@ -var v = [1,2,3]; -var r = range(v); -for_each(r, fun(x) { assert_equal(true, x>0); } ) diff --git a/unittests/3.x/for_each_retro.chai b/unittests/3.x/for_each_retro.chai deleted file mode 100644 index cc27a580..00000000 --- a/unittests/3.x/for_each_retro.chai +++ /dev/null @@ -1,4 +0,0 @@ -// Don't bother checking the output from this one, just makes sure it executes -var v = [1,2,3]; -var r = retro(range(v)); -for_each(r, print) diff --git a/unittests/3.x/function_array_adjacent.chai b/unittests/3.x/function_array_adjacent.chai deleted file mode 100644 index c34e2be9..00000000 --- a/unittests/3.x/function_array_adjacent.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, `+`.get_contained_functions()[0].get_arity()) diff --git a/unittests/3.x/function_introspection.chai b/unittests/3.x/function_introspection.chai deleted file mode 100644 index 014cf45d..00000000 --- a/unittests/3.x/function_introspection.chai +++ /dev/null @@ -1,75 +0,0 @@ - -#Test Function Description -def test_function(a) -{ - return a; -} - - - - -// test_function tests -assert_equal(test_function.get_arity(), 1); -assert_equal(test_function.get_contained_functions().size(), 0); -assert_equal(test_function.get_param_types().size(), 2); - -assert_equal(test_function, test_function); - -assert_not_equal(test_function, `+`); - -assert_equal(test_function.call([1]), 1); - -// dynamic object function tests - -def int::test_fun() -{ - return this; -} - -assert_equal(test_fun.get_arity(), 1); -assert_equal(test_fun.get_contained_functions.size(), 1); -assert_equal(test_fun.get_param_types().size(), 2); -assert_equal(test_fun, test_fun); -var test_fun_types = test_fun.get_param_types(); -assert_equal(true, test_fun_types[0].bare_equal(Object_type)); -assert_equal(true, test_fun_types[1].bare_equal(int_type)); - - -// built-ins tests - -assert_equal(2, `==`.get_arity()); - -// < should be the merging of two functions bool <(PODObject, PODObject) and bool <(string, string) -// we want to peel it apart and make sure that's true -var types = `<`.get_param_types(); -assert_equal(3, types.size()); -assert_equal(true, types[0].bare_equal(bool_type)); -assert_equal(true, types[1].bare_equal(Object_type)); -assert_equal(true, types[2].bare_equal(Object_type)); -assert_equal(2, `<`.get_contained_functions().size()); - - -// guard existence tests - -def with_guard(x) : x > 3 {} -def without_guard(x) {} - -def group_guard(x) {} -def group_guard(x) : x > 3 {} - -assert_equal(true, with_guard.has_guard()); -assert_equal(false, without_guard.has_guard()); - -assert_equal(2, group_guard.get_contained_functions().size()); -var group = group_guard.get_contained_functions(); - -assert_equal(true, group[0].has_guard()) -assert_equal(false, group[1].has_guard()) - -assert_throws("Function does not have a guard", fun[group]() { group[1].get_guard(); } ); -assert_throws("Function does not have a guard", fun[without_guard]() { without_guard.get_guard(); } ); - -var guard = with_guard.get_guard(); - -assert_equal(false, guard.has_guard()); -assert_throws("Function does not have a guard", fun[guard]() { guard.get_guard(); } ); diff --git a/unittests/3.x/function_reassignment.chai b/unittests/3.x/function_reassignment.chai deleted file mode 100644 index 2a885fdc..00000000 --- a/unittests/3.x/function_reassignment.chai +++ /dev/null @@ -1,3 +0,0 @@ -var x = `+` -x = `-` -assert_equal(1, x(5,4)) diff --git a/unittests/3.x/generate_range.chai b/unittests/3.x/generate_range.chai deleted file mode 100644 index 9e25970a..00000000 --- a/unittests/3.x/generate_range.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([1,2,3,4,5,6,7,8,9,10], generate_range(1, 10)) diff --git a/unittests/3.x/global_const_in_module.chai b/unittests/3.x/global_const_in_module.chai deleted file mode 100644 index c9ca65a6..00000000 --- a/unittests/3.x/global_const_in_module.chai +++ /dev/null @@ -1,7 +0,0 @@ -load_module("test_module") - - -assert_equal(to_int(TestValue1), 1) - -assert_equal(TestValue1.type_name(), "TestEnum") - diff --git a/unittests/3.x/if.chai b/unittests/3.x/if.chai deleted file mode 100644 index 3ec7321b..00000000 --- a/unittests/3.x/if.chai +++ /dev/null @@ -1,7 +0,0 @@ -var t = false; - -if (true) { - t = true; -} - -assert_equal(true, t); diff --git a/unittests/3.x/if_else.chai b/unittests/3.x/if_else.chai deleted file mode 100644 index 8cb42db9..00000000 --- a/unittests/3.x/if_else.chai +++ /dev/null @@ -1,13 +0,0 @@ -var i = 3 -var b1 = false; -var b2 = false; - -if (i == 2) { - b1 = true; -} -else { - b2 = true; -} - -assert_equal(false, b1); -assert_equal(true, b2); diff --git a/unittests/3.x/if_elseif.chai b/unittests/3.x/if_elseif.chai deleted file mode 100644 index 75b85b5f..00000000 --- a/unittests/3.x/if_elseif.chai +++ /dev/null @@ -1,18 +0,0 @@ -var b1 = false; -var b2 = false; -var b3 = false; - -var i = 3 -if (i == 2) { - b1 = true; -} -else if (i == 4) { - b2 = true; -} -else if (i == 3) { - b3 = true; -} - -assert_equal(false, b1); -assert_equal(false, b2); -assert_equal(true, b3); diff --git a/unittests/3.x/if_elseif_else.chai b/unittests/3.x/if_elseif_else.chai deleted file mode 100644 index 26ed0d26..00000000 --- a/unittests/3.x/if_elseif_else.chai +++ /dev/null @@ -1,14 +0,0 @@ -var i = 3 -var b = false -if (i == 2) { - assert_equal(false, true) -} -else if (i == 4) { - assert_equal(false, true) -} -else { - assert_equal(true, true) - b = true -} - -assert_equal(true, b) diff --git a/unittests/3.x/index_operator.chai b/unittests/3.x/index_operator.chai deleted file mode 100644 index e8af5cf6..00000000 --- a/unittests/3.x/index_operator.chai +++ /dev/null @@ -1,10 +0,0 @@ - -// tests more complex parses of the index operator - -def Bob::bob3() { return [1,2,3]; } -def Bob::Bob() {} -var b = Bob(); - - -assert_equal(b.bob3()[0], 1); -assert_equal((b.bob3())[1], 2); diff --git a/unittests/3.x/inheritance.chai b/unittests/3.x/inheritance.chai deleted file mode 100644 index 1fcd346b..00000000 --- a/unittests/3.x/inheritance.chai +++ /dev/null @@ -1,8 +0,0 @@ -load_module("test_module") - -var t0 = TestBaseType() -var t = TestDerivedType(); - -assert_equal(t0.func(), 0); -assert_equal(t.func(), 1); - diff --git a/unittests/3.x/instring_eval.chai b/unittests/3.x/instring_eval.chai deleted file mode 100644 index a72b2fc4..00000000 --- a/unittests/3.x/instring_eval.chai +++ /dev/null @@ -1,3 +0,0 @@ -var bob = 5.5 -assert_equal("5.5", "${bob}") -assert_equal("val: 8 and 8", "val: ${5.5 + 2.5} and ${bob + 2.5}") diff --git a/unittests/3.x/instring_eval_more.chai b/unittests/3.x/instring_eval_more.chai deleted file mode 100644 index 17768f82..00000000 --- a/unittests/3.x/instring_eval_more.chai +++ /dev/null @@ -1,4 +0,0 @@ -assert_equal("\$ {4 + 5}", "$ {4 + 5}") -assert_equal("\$9", "$${4+5}") -assert_equal("Value: \${4 + 5}", "Value: \${4 + 5}") -assert_equal("Value: \$9", "Value: \$${4 + 5}") diff --git a/unittests/3.x/invalid_function_assignment.chai b/unittests/3.x/invalid_function_assignment.chai deleted file mode 100644 index 3aa1bc2f..00000000 --- a/unittests/3.x/invalid_function_assignment.chai +++ /dev/null @@ -1 +0,0 @@ -assert_throws("Error: \"Error, cannot assign to constant value.\"", fun() { clone = `-` } ); diff --git a/unittests/3.x/invalid_function_reassignment.chai b/unittests/3.x/invalid_function_reassignment.chai deleted file mode 100644 index 77307b6f..00000000 --- a/unittests/3.x/invalid_function_reassignment.chai +++ /dev/null @@ -1,2 +0,0 @@ -assert_throws("Error: \"Unable to find appropriate'=' operator.\" With parameters: (int, const Function)", fun() { auto x = 5; x = `-`; } ); - diff --git a/unittests/3.x/is_undef.chai b/unittests/3.x/is_undef.chai deleted file mode 100644 index 38572f0f..00000000 --- a/unittests/3.x/is_undef.chai +++ /dev/null @@ -1,4 +0,0 @@ -var i; -assert_equal(true, i.is_var_undef()); -i = 5; -assert_equal(false, i.is_var_undef()); diff --git a/unittests/3.x/join.chai b/unittests/3.x/join.chai deleted file mode 100644 index 1891c468..00000000 --- a/unittests/3.x/join.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal("1*2*3", join([1, 2, 3], "*")) diff --git a/unittests/3.x/lambda.chai b/unittests/3.x/lambda.chai deleted file mode 100644 index 6b65a1ba..00000000 --- a/unittests/3.x/lambda.chai +++ /dev/null @@ -1,2 +0,0 @@ -var bob = fun(x) { x + 1 } -assert_equal(4, bob(3)); diff --git a/unittests/3.x/list_push_back.chai b/unittests/3.x/list_push_back.chai deleted file mode 100644 index 4d88deb8..00000000 --- a/unittests/3.x/list_push_back.chai +++ /dev/null @@ -1,8 +0,0 @@ -load_module("stl_extra") - -var x = List() -x.push_back(3) -x.push_back("A") - -assert_equal(3, x.front()); -assert_equal("A", x.back()); diff --git a/unittests/3.x/list_push_front.chai b/unittests/3.x/list_push_front.chai deleted file mode 100644 index 86e28329..00000000 --- a/unittests/3.x/list_push_front.chai +++ /dev/null @@ -1,8 +0,0 @@ -load_module("stl_extra") - -var x = List() -x.push_front(3) -x.push_front("A") - -assert_equal("A", x.front()); -assert_equal(3, x.back()); diff --git a/unittests/3.x/load_module.chai b/unittests/3.x/load_module.chai deleted file mode 100644 index a231a200..00000000 --- a/unittests/3.x/load_module.chai +++ /dev/null @@ -1,2 +0,0 @@ -load_module("test_module") -assert_equal("Hello World", hello_world()); diff --git a/unittests/3.x/loop_inner_outer.chai b/unittests/3.x/loop_inner_outer.chai deleted file mode 100644 index 64a25e6e..00000000 --- a/unittests/3.x/loop_inner_outer.chai +++ /dev/null @@ -1,9 +0,0 @@ -var total = 0 - -for (var i = 0; i < 10; ++i) { - for (var j = 0; j < 10; ++j) { - total += 1 - } -} - -assert_equal(100, total); diff --git a/unittests/3.x/malformed_inline_map.chai b/unittests/3.x/malformed_inline_map.chai deleted file mode 100644 index 1488ded4..00000000 --- a/unittests/3.x/malformed_inline_map.chai +++ /dev/null @@ -1,8 +0,0 @@ - -try { - eval("[\"hello\":5,\"j\",\"k\"]"); - assert_true(false); -} catch (eval_error ee) { -} - - diff --git a/unittests/3.x/map.chai b/unittests/3.x/map.chai deleted file mode 100644 index a0a31ee1..00000000 --- a/unittests/3.x/map.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([true, false, true], map([1,2,3], odd)) diff --git a/unittests/3.x/map_access.chai b/unittests/3.x/map_access.chai deleted file mode 100644 index 19ebc1ad..00000000 --- a/unittests/3.x/map_access.chai +++ /dev/null @@ -1,2 +0,0 @@ -var x = ["bob":2, "fred":3] -assert_equal(3, x["fred"]) diff --git a/unittests/3.x/map_inplace_init.chai b/unittests/3.x/map_inplace_init.chai deleted file mode 100644 index b1d8221b..00000000 --- a/unittests/3.x/map_inplace_init.chai +++ /dev/null @@ -1,3 +0,0 @@ -var x = ["bob":1, "fred":2] - -assert_equal(2, x.size()); diff --git a/unittests/3.x/math_add.chai b/unittests/3.x/math_add.chai deleted file mode 100644 index bcb90369..00000000 --- a/unittests/3.x/math_add.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3, (1 + 2)) diff --git a/unittests/3.x/math_add_mixed.chai b/unittests/3.x/math_add_mixed.chai deleted file mode 100644 index b000cafe..00000000 --- a/unittests/3.x/math_add_mixed.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3.5, 1.5 + 2) diff --git a/unittests/3.x/math_dec.chai b/unittests/3.x/math_dec.chai deleted file mode 100644 index e746f298..00000000 --- a/unittests/3.x/math_dec.chai +++ /dev/null @@ -1,3 +0,0 @@ -var i = 3 -assert_equal(2, --i) -assert_equal(2, i) diff --git a/unittests/3.x/math_div.chai b/unittests/3.x/math_div.chai deleted file mode 100644 index 971f2170..00000000 --- a/unittests/3.x/math_div.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, 10/5) diff --git a/unittests/3.x/math_inc.chai b/unittests/3.x/math_inc.chai deleted file mode 100644 index ec317c03..00000000 --- a/unittests/3.x/math_inc.chai +++ /dev/null @@ -1,3 +0,0 @@ -var i = 3 -assert_equal(4, ++i) -assert_equal(4, i) diff --git a/unittests/3.x/math_mod.chai b/unittests/3.x/math_mod.chai deleted file mode 100644 index 326c35a9..00000000 --- a/unittests/3.x/math_mod.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, 11 % 3) diff --git a/unittests/3.x/math_mult.chai b/unittests/3.x/math_mult.chai deleted file mode 100644 index 94c355d6..00000000 --- a/unittests/3.x/math_mult.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(12, 3 * 4) diff --git a/unittests/3.x/math_negate.chai b/unittests/3.x/math_negate.chai deleted file mode 100644 index 36bae880..00000000 --- a/unittests/3.x/math_negate.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(-7, -(3 + 4)) diff --git a/unittests/3.x/math_paren.chai b/unittests/3.x/math_paren.chai deleted file mode 100644 index 01b7f205..00000000 --- a/unittests/3.x/math_paren.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(27, 3*(4+5)) diff --git a/unittests/3.x/math_sub.chai b/unittests/3.x/math_sub.chai deleted file mode 100644 index 7e8342c1..00000000 --- a/unittests/3.x/math_sub.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, 5 - 3) diff --git a/unittests/3.x/max.chai b/unittests/3.x/max.chai deleted file mode 100644 index 533e7e84..00000000 --- a/unittests/3.x/max.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(5, max(3, 5)) diff --git a/unittests/3.x/memberscope.chai b/unittests/3.x/memberscope.chai deleted file mode 100644 index fe46810a..00000000 --- a/unittests/3.x/memberscope.chai +++ /dev/null @@ -1,12 +0,0 @@ -attr Vector3::x -attr Vector3::y -attr Vector3::z - -def Vector3::Vector3(x, y, z) { - this.x = x - this.y = y - this.z = z -} - -var v = Vector3(1,2,3); -assert_equal(1, v.x); diff --git a/unittests/3.x/method_sugar.chai b/unittests/3.x/method_sugar.chai deleted file mode 100644 index 521400bc..00000000 --- a/unittests/3.x/method_sugar.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(6, [1, 2, 3].sum()) diff --git a/unittests/3.x/min.chai b/unittests/3.x/min.chai deleted file mode 100644 index 0ef1ba79..00000000 --- a/unittests/3.x/min.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3, min(3, 5)) diff --git a/unittests/3.x/mmd1.chai b/unittests/3.x/mmd1.chai deleted file mode 100644 index 36121a90..00000000 --- a/unittests/3.x/mmd1.chai +++ /dev/null @@ -1,20 +0,0 @@ -def bob(x, y, z) { - x + y + z -} - -def bob(x, y) { - x - y -} - -def bob(x) { - -x -} - -def bob() { - 10 -} - -assert_equal(10, bob()) -assert_equal(-5, bob(5)) -assert_equal(-1, bob(5,6)) -assert_equal(18, bob(5,6,7)) diff --git a/unittests/3.x/mmd2.chai b/unittests/3.x/mmd2.chai deleted file mode 100644 index 1c5f1771..00000000 --- a/unittests/3.x/mmd2.chai +++ /dev/null @@ -1,9 +0,0 @@ -def bob(x, y) : x > 10 { x - y } - -def bob(x, y) : x > 5 { x - y + 10 } - -def bob(x, y) { x + y } - -assert_equal(7, bob(3,4)) -assert_equal(9, bob(6,7)) -assert_equal(-1, bob(11,12)) diff --git a/unittests/3.x/multiline.chai b/unittests/3.x/multiline.chai deleted file mode 100644 index f13be4e6..00000000 --- a/unittests/3.x/multiline.chai +++ /dev/null @@ -1,9 +0,0 @@ -var x = [1, 2, - 3, 4] - -assert_equal(1, x[0]) - -var y = map(x, - fun(x) { x + 1 }) - -assert_equal(2, y[0]) diff --git a/unittests/3.x/number_formats.chai b/unittests/3.x/number_formats.chai deleted file mode 100644 index c80ece04..00000000 --- a/unittests/3.x/number_formats.chai +++ /dev/null @@ -1,3 +0,0 @@ -assert_equal(10, 012) -assert_equal(31, 0x1f) -assert_equal(3, 0b11) diff --git a/unittests/3.x/object_attr.chai b/unittests/3.x/object_attr.chai deleted file mode 100644 index c2da08ea..00000000 --- a/unittests/3.x/object_attr.chai +++ /dev/null @@ -1,6 +0,0 @@ -attr bob::z -def bob::bob() { this.z = 10 } -var x = bob() -def bob::fred(x) { this.z - x } - -assert_equal(7, x.fred(3)) diff --git a/unittests/3.x/object_attr_same_name.chai b/unittests/3.x/object_attr_same_name.chai deleted file mode 100644 index fa20bac4..00000000 --- a/unittests/3.x/object_attr_same_name.chai +++ /dev/null @@ -1,9 +0,0 @@ -attr bob::z -def bob::bob() { this.z = 10 } - -attr bob2::z -def bob2::bob2() { this.z = 12 } - -var b = bob(); -var b2 = bob2(); - diff --git a/unittests/3.x/object_clone.chai b/unittests/3.x/object_clone.chai deleted file mode 100644 index 4659f41a..00000000 --- a/unittests/3.x/object_clone.chai +++ /dev/null @@ -1,11 +0,0 @@ -attr bob::z -def bob::bob() { } - -var x = bob(); -x.z = 10; - -var y = clone(x); -y.z = 20; - -assert_equal(10, x.z) -assert_equal(20, y.z) diff --git a/unittests/3.x/object_constructor_guards.chai b/unittests/3.x/object_constructor_guards.chai deleted file mode 100644 index f48c00a1..00000000 --- a/unittests/3.x/object_constructor_guards.chai +++ /dev/null @@ -1,10 +0,0 @@ -attr bob::val - -def bob::bob(x) : x < 10 { this.val = "Less Than Ten: " + x.to_string(); } -def bob::bob(x) { this.val = "Any Other Value: " + x.to_string(); } - -var b = bob(12) -var c = bob(3) - -assert_equal("Any Other Value: 12", b.val ) -assert_equal("Less Than Ten: 3", c.val ) diff --git a/unittests/3.x/object_method_guards.chai b/unittests/3.x/object_method_guards.chai deleted file mode 100644 index addc8508..00000000 --- a/unittests/3.x/object_method_guards.chai +++ /dev/null @@ -1,7 +0,0 @@ -def bob::bob() { } -def bob::fred(e) : e < 10 { assert_equal(true, e<10) } -def bob::fred(e) { assert_equal(true, e >= 10) } - -var b = bob() -b.fred(3) -b.fred(12) diff --git a/unittests/3.x/odd.chai b/unittests/3.x/odd.chai deleted file mode 100644 index dbaf2a01..00000000 --- a/unittests/3.x/odd.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(false, odd(4)) diff --git a/unittests/3.x/operator_overload.chai b/unittests/3.x/operator_overload.chai deleted file mode 100644 index 9bd2eb79..00000000 --- a/unittests/3.x/operator_overload.chai +++ /dev/null @@ -1,9 +0,0 @@ -def Bob::`+`(y) { this.x + y.x } -def Bob::Bob() { } -attr Bob::x -var b = Bob() -var c = Bob() -b.x = 4 -c.x = 5 - -assert_equal(9, b+c) diff --git a/unittests/3.x/operator_overload2.chai b/unittests/3.x/operator_overload2.chai deleted file mode 100644 index b4afbe7b..00000000 --- a/unittests/3.x/operator_overload2.chai +++ /dev/null @@ -1,9 +0,0 @@ -def Bob::Bob() { } -attr Bob::x -def `-`(a, b) : is_type(a, "Bob") && is_type(b, "Bob") { a.x - b.x } -var b = Bob() -var c = Bob() -b.x = 4 -c.x = 5 - -assert_equal(-1, b-c) diff --git a/unittests/3.x/operators_float.chai b/unittests/3.x/operators_float.chai deleted file mode 100644 index 847f5e58..00000000 --- a/unittests/3.x/operators_float.chai +++ /dev/null @@ -1,16 +0,0 @@ -var i = 1.0; -var j = 2.0; -var k = 3.0; - -assert_equal(3, i + j) -assert_equal(1.0, +i) -assert_equal(-1, i-j) -assert_equal(-1, -i) -assert_equal(1.5, k/j) -assert_equal(6, j*k) - -assert_equal(0, i -= i) -assert_equal(3, j *= 1.5) -assert_equal(1.5, j /= 2) -assert_equal(2.5, j += 1) -assert_throws("Error: \"Error with numeric operator calling: %\"", fun[k]() { k % 2 } ); diff --git a/unittests/3.x/operators_int.chai b/unittests/3.x/operators_int.chai deleted file mode 100644 index 4627b552..00000000 --- a/unittests/3.x/operators_int.chai +++ /dev/null @@ -1,31 +0,0 @@ -var i = 1; -var j = 2; -var k = 3; - -assert_equal(3, i + j); -assert_equal(1, +i); -assert_equal(-1, i - j); -assert_equal(-1, -i); -assert_equal(2, j & k); -assert_equal(-3, ~j); -assert_equal(1, j ^ k); -assert_equal(3, i | j); -assert_equal(2, j / i); -assert_equal(4, i << j); -assert_equal(6, j * k); -assert_equal(1, k % j); -assert_equal(1, j >> i); - -assert_equal(0, i &= 2); -assert_equal(1, j ^= 3); -assert_equal(3, j |= 2); -assert_equal(-1, i -= 1); -assert_equal(6, j <<= 1); -assert_equal(12, j *= 2); -assert_equal(6, j /= 2); -assert_equal(2, j %= 4); -assert_equal(1, j >>= 1); -assert_equal(2, j += 1); -assert_equal(1, --j); -assert_equal(2, ++j); - diff --git a/unittests/3.x/pair.chai b/unittests/3.x/pair.chai deleted file mode 100644 index 9b3c8049..00000000 --- a/unittests/3.x/pair.chai +++ /dev/null @@ -1,5 +0,0 @@ -var p = Pair("Hello", "World") - -assert_equal(p.first, "Hello") -assert_equal(p.second, "World") - diff --git a/unittests/3.x/pointer_passed_to_constructor.chai b/unittests/3.x/pointer_passed_to_constructor.chai deleted file mode 100644 index 6495ee38..00000000 --- a/unittests/3.x/pointer_passed_to_constructor.chai +++ /dev/null @@ -1,8 +0,0 @@ -load_module("test_module") - -var i = 1; -var t0 = TestBaseType(i); - -var t1 = TestBaseType(get_new_int()) - - diff --git a/unittests/3.x/precedence_1.chai b/unittests/3.x/precedence_1.chai deleted file mode 100644 index a5388625..00000000 --- a/unittests/3.x/precedence_1.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(14, 2 + 3 * 4) diff --git a/unittests/3.x/precedence_2.chai b/unittests/3.x/precedence_2.chai deleted file mode 100644 index 27a19d4a..00000000 --- a/unittests/3.x/precedence_2.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(-2, 5 - 4 - 3) diff --git a/unittests/3.x/precedence_3.chai b/unittests/3.x/precedence_3.chai deleted file mode 100644 index 6eecbf64..00000000 --- a/unittests/3.x/precedence_3.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(0, 10 / 5 % 2) diff --git a/unittests/3.x/precedence_eq.chai b/unittests/3.x/precedence_eq.chai deleted file mode 100644 index 325d667e..00000000 --- a/unittests/3.x/precedence_eq.chai +++ /dev/null @@ -1,3 +0,0 @@ -var x = var y = 4 -assert_equal(4, x); -assert_equal(4, y); diff --git a/unittests/3.x/product.chai b/unittests/3.x/product.chai deleted file mode 100644 index 03ba3cfa..00000000 --- a/unittests/3.x/product.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(24, product([1, 2, 3, 4])) diff --git a/unittests/3.x/range.chai b/unittests/3.x/range.chai deleted file mode 100644 index ddef4f2a..00000000 --- a/unittests/3.x/range.chai +++ /dev/null @@ -1,4 +0,0 @@ -var x = [1, 2, 3, 4] -var r = range(x) -r.pop_front() -assert_equal(2, r.front()); diff --git a/unittests/3.x/range_back.chai b/unittests/3.x/range_back.chai deleted file mode 100644 index 6bf56721..00000000 --- a/unittests/3.x/range_back.chai +++ /dev/null @@ -1,4 +0,0 @@ -var x = [1, 2, 3, 4] -var r = range(x) -r.pop_back() -assert_equal(3, r.back()) diff --git a/unittests/3.x/range_contains.chai b/unittests/3.x/range_contains.chai deleted file mode 100644 index 28a99b12..00000000 --- a/unittests/3.x/range_contains.chai +++ /dev/null @@ -1,5 +0,0 @@ -var v = [1,2,"hi", "world", 5.5] -assert_equal(true, v.contains(5.5)) -assert_equal(false, v.contains(0)) -assert_equal(false, v.contains(1, lt)) -assert_equal(true, v.contains(2, `==`)) diff --git a/unittests/3.x/range_find.chai b/unittests/3.x/range_find.chai deleted file mode 100644 index 08045e5a..00000000 --- a/unittests/3.x/range_find.chai +++ /dev/null @@ -1,6 +0,0 @@ -var v = [2, 1, "Hi", 5.5] -var r = v.find("Hi"); - -assert_equal("Hi", r.front()) -var r2 = v.find(2, `<`); -assert_equal(1, r2.front()); diff --git a/unittests/3.x/range_inplace.chai b/unittests/3.x/range_inplace.chai deleted file mode 100644 index d661f5de..00000000 --- a/unittests/3.x/range_inplace.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([3,4,5,6], [3..6]) diff --git a/unittests/3.x/reduce.chai b/unittests/3.x/reduce.chai deleted file mode 100644 index 3b255b31..00000000 --- a/unittests/3.x/reduce.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(10, reduce([1, 2, 3, 4], `+`)) diff --git a/unittests/3.x/ref_equal.chai b/unittests/3.x/ref_equal.chai deleted file mode 100644 index 1dbb90e1..00000000 --- a/unittests/3.x/ref_equal.chai +++ /dev/null @@ -1,5 +0,0 @@ -var i = 3 -var j := i -j = 4 - -assert_equal(4, i) diff --git a/unittests/3.x/retro.chai b/unittests/3.x/retro.chai deleted file mode 100644 index d7f6818d..00000000 --- a/unittests/3.x/retro.chai +++ /dev/null @@ -1,4 +0,0 @@ -var x = [1, 2, 3, 4] -var r = retro(range(x)) -r.pop_front() -assert_equal(3, r.front()) diff --git a/unittests/3.x/retroretro.chai b/unittests/3.x/retroretro.chai deleted file mode 100644 index 09af3ca7..00000000 --- a/unittests/3.x/retroretro.chai +++ /dev/null @@ -1,7 +0,0 @@ -var x = [1, 2, 3, 4] -var r = retro(range(x)) -r.pop_back() -var r2 = retro(r) -r2.pop_front() -assert_equal(2, r.back()) -assert_equal(3, r2.front()) diff --git a/unittests/3.x/return.chai b/unittests/3.x/return.chai deleted file mode 100644 index 1747de90..00000000 --- a/unittests/3.x/return.chai +++ /dev/null @@ -1,5 +0,0 @@ -def sam() { - return 5 -} - -assert_equal(5, sam()) diff --git a/unittests/3.x/runtime_error.chai b/unittests/3.x/runtime_error.chai deleted file mode 100644 index e1e2fc10..00000000 --- a/unittests/3.x/runtime_error.chai +++ /dev/null @@ -1,11 +0,0 @@ -var caught = false - -try { - throw(runtime_error("error")) -} -catch(e) { - caught = true - assert_equal("error", e.what()) -} - -assert_equal(true, caught) diff --git a/unittests/3.x/shift.chai b/unittests/3.x/shift.chai deleted file mode 100644 index 03f7ea3a..00000000 --- a/unittests/3.x/shift.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(8, 2 << 2) diff --git a/unittests/3.x/string_charptr.chai b/unittests/3.x/string_charptr.chai deleted file mode 100644 index a3065ce4..00000000 --- a/unittests/3.x/string_charptr.chai +++ /dev/null @@ -1,6 +0,0 @@ -assert_equal(true, "hello".c_str().is_var_const()) -assert_equal("char", "hello".c_str().type_name()) - -assert_equal(true, "hello".data().is_var_const()) -assert_equal("char", "hello".data().type_name()) - diff --git a/unittests/3.x/string_concat.chai b/unittests/3.x/string_concat.chai deleted file mode 100644 index 40bf4aaf..00000000 --- a/unittests/3.x/string_concat.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal("hello, there", "hello, " + "there") diff --git a/unittests/3.x/string_find.chai b/unittests/3.x/string_find.chai deleted file mode 100644 index f2cc1f82..00000000 --- a/unittests/3.x/string_find.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3, find("123abab", "ab")) diff --git a/unittests/3.x/string_find_first_not_of.chai b/unittests/3.x/string_find_first_not_of.chai deleted file mode 100644 index 4d5fd8d4..00000000 --- a/unittests/3.x/string_find_first_not_of.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, find_first_not_of("abcd", "abd")) diff --git a/unittests/3.x/string_find_first_of.chai b/unittests/3.x/string_find_first_of.chai deleted file mode 100644 index 0200bb2c..00000000 --- a/unittests/3.x/string_find_first_of.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(1, find_first_of("abab", "bec")) diff --git a/unittests/3.x/string_find_last_not_of.chai b/unittests/3.x/string_find_last_not_of.chai deleted file mode 100644 index 0090f9f9..00000000 --- a/unittests/3.x/string_find_last_not_of.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3, find_last_not_of("abab", "ac")) diff --git a/unittests/3.x/string_find_last_of.chai b/unittests/3.x/string_find_last_of.chai deleted file mode 100644 index 72f0f6a0..00000000 --- a/unittests/3.x/string_find_last_of.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(3, find_last_of("abab", "bec")) diff --git a/unittests/3.x/string_init.chai b/unittests/3.x/string_init.chai deleted file mode 100644 index a3d11632..00000000 --- a/unittests/3.x/string_init.chai +++ /dev/null @@ -1 +0,0 @@ -print("bob") diff --git a/unittests/3.x/string_literal_access.chai b/unittests/3.x/string_literal_access.chai deleted file mode 100644 index e8943a1b..00000000 --- a/unittests/3.x/string_literal_access.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal('b', "abc"[1]) diff --git a/unittests/3.x/string_rfind.chai b/unittests/3.x/string_rfind.chai deleted file mode 100644 index 01675f34..00000000 --- a/unittests/3.x/string_rfind.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(5, rfind("123abab", "ab")) diff --git a/unittests/3.x/sum.chai b/unittests/3.x/sum.chai deleted file mode 100644 index 7502ae7e..00000000 --- a/unittests/3.x/sum.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(10, sum([1, 2, 3, 4])) diff --git a/unittests/3.x/switch_break.chai b/unittests/3.x/switch_break.chai deleted file mode 100644 index 8d362759..00000000 --- a/unittests/3.x/switch_break.chai +++ /dev/null @@ -1,22 +0,0 @@ -var total = 0; - -switch(2) { - case (1) { - total += 1; - break; - } - case (2) { - total += 2; - break; - } - case (3) { - total += 4; - break; - } - case (4) { - total += 8; - break; - } -} - -assert_equal(total, 2) diff --git a/unittests/3.x/switch_default.chai b/unittests/3.x/switch_default.chai deleted file mode 100644 index 8c48aa50..00000000 --- a/unittests/3.x/switch_default.chai +++ /dev/null @@ -1,18 +0,0 @@ -var total = 0; - -switch(2) { - case (1) { - total += 1; - } - case (3) { - total += 4; - } - case (4) { - total += 8; - } - default { - total += 16; - } -} - -assert_equal(total, 16) diff --git a/unittests/3.x/switch_empty.chai b/unittests/3.x/switch_empty.chai deleted file mode 100644 index 8d3a1669..00000000 --- a/unittests/3.x/switch_empty.chai +++ /dev/null @@ -1,4 +0,0 @@ -switch(true) { } - -// We just have to get here without error for success -assert_equal(true, true); diff --git a/unittests/3.x/switch_fallthru.chai b/unittests/3.x/switch_fallthru.chai deleted file mode 100644 index 627b6493..00000000 --- a/unittests/3.x/switch_fallthru.chai +++ /dev/null @@ -1,18 +0,0 @@ -var total = 0; - -switch(2) { - case (1) { - total += 1; - } - case (2) { - total += 2; - } - case (3) { - total += 4; - } - case (4) { - total += 8; - } -} - -assert_equal(total, 14); diff --git a/unittests/3.x/switch_fallthru_and_break.chai b/unittests/3.x/switch_fallthru_and_break.chai deleted file mode 100644 index 3c93071b..00000000 --- a/unittests/3.x/switch_fallthru_and_break.chai +++ /dev/null @@ -1,19 +0,0 @@ -var total = 0; - -switch(2) { - case (1) { - total += 1; - } - case (2) { - total += 2; - } - case (3) { - total += 4; - break; - } - case (4) { - total += 8; - } -} - -assert_equal(total, 6) diff --git a/unittests/3.x/take.chai b/unittests/3.x/take.chai deleted file mode 100644 index 5110392e..00000000 --- a/unittests/3.x/take.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(2, take([1, 2, 3, 4], 2).back()) diff --git a/unittests/3.x/take_while.chai b/unittests/3.x/take_while.chai deleted file mode 100644 index 56a4ba22..00000000 --- a/unittests/3.x/take_while.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal([1], take_while([1, 2, 3, 4], odd)) diff --git a/unittests/3.x/type_info.chai b/unittests/3.x/type_info.chai deleted file mode 100644 index fc2dd3e7..00000000 --- a/unittests/3.x/type_info.chai +++ /dev/null @@ -1,11 +0,0 @@ -assert_equal("string", string_type.name()); -assert_equal(false, string_type.is_type_const()); -assert_equal(false, string_type.is_type_reference()); -assert_equal(false, string_type.is_type_void()); -assert_equal(false, string_type.is_type_undef()); -assert_equal(false, string_type.is_type_pointer()); -assert_equal("string", "string".get_type_info().name()); -assert_equal(true, string_type.bare_equal("string".get_type_info())); - -assert_equal(true, "bob".is_type(string_type)); - diff --git a/unittests/3.x/unit_test.inc b/unittests/3.x/unit_test.inc deleted file mode 100644 index d746e7bf..00000000 --- a/unittests/3.x/unit_test.inc +++ /dev/null @@ -1,53 +0,0 @@ -def assert_equal(x, y) -{ - if (x == y) - { - // Passes - } else { - // Fails - print("assert_equal failure: got " + to_string(y) + " expected " + to_string(x)); - exit(-1); - } -} - -def assert_false(f) -{ - if (f) - { - print("assert_false failure"); - exit(-1); - } -} - -def assert_true(f) -{ - if (!f) - { - print("assert_false failure"); - exit(-1); - } -} - -def assert_not_equal(x, y) -{ - if (!(x == y)) - { - // Passes - } else { - // Fails - print("assert_not_equal failure: got " + to_string(y) + " which was not expected."); - exit(-1); - } -} - -def assert_throws(desc, x) -{ - if (throws_exception(x)) - { - // Passes - } else { - // Fails - print("assert_throws failure, function did not throw exception: " + to_string(desc)); - exit(-1); - } -} diff --git a/unittests/3.x/use.chai b/unittests/3.x/use.chai deleted file mode 100644 index efd587da..00000000 --- a/unittests/3.x/use.chai +++ /dev/null @@ -1,9 +0,0 @@ -use("use.inc") - -assert_equal("hello", greet()) - -// Include it a second time and see if there are any errors -use("use.inc") - -assert_equal("hello", greet()) - diff --git a/unittests/3.x/use.inc b/unittests/3.x/use.inc deleted file mode 100644 index 28970935..00000000 --- a/unittests/3.x/use.inc +++ /dev/null @@ -1,4 +0,0 @@ -def greet { - return("hello") -} - diff --git a/unittests/3.x/vector_access.chai b/unittests/3.x/vector_access.chai deleted file mode 100644 index 34d483cd..00000000 --- a/unittests/3.x/vector_access.chai +++ /dev/null @@ -1,2 +0,0 @@ -var x = [1, 2, 3] -assert_equal(3, x[2]) diff --git a/unittests/3.x/vector_erase_at.chai b/unittests/3.x/vector_erase_at.chai deleted file mode 100644 index 9a96218f..00000000 --- a/unittests/3.x/vector_erase_at.chai +++ /dev/null @@ -1,3 +0,0 @@ -var x = [1, 2, 3] -x.erase_at(1) -assert_equal([1,3], x); diff --git a/unittests/3.x/vector_inplace_init.chai b/unittests/3.x/vector_inplace_init.chai deleted file mode 100644 index f16c15b3..00000000 --- a/unittests/3.x/vector_inplace_init.chai +++ /dev/null @@ -1,2 +0,0 @@ -var x = [1, 2, 3] -assert_equal(3, x.size()) diff --git a/unittests/3.x/vector_insert_at.chai b/unittests/3.x/vector_insert_at.chai deleted file mode 100644 index 4f6ec45b..00000000 --- a/unittests/3.x/vector_insert_at.chai +++ /dev/null @@ -1,3 +0,0 @@ -var x = [1, 2, 3] -x.insert_at(1, 6) -assert_equal([1,6,2,3], x); diff --git a/unittests/3.x/vector_literal_acccess.chai b/unittests/3.x/vector_literal_acccess.chai deleted file mode 100644 index 29a7c05f..00000000 --- a/unittests/3.x/vector_literal_acccess.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(1, [1,2,3][0]) diff --git a/unittests/3.x/vector_of_one.chai b/unittests/3.x/vector_of_one.chai deleted file mode 100644 index f4bb01bf..00000000 --- a/unittests/3.x/vector_of_one.chai +++ /dev/null @@ -1,2 +0,0 @@ -var x = [1] -assert_equal(1, x[0]) diff --git a/unittests/3.x/vector_paren_literal_access.chai b/unittests/3.x/vector_paren_literal_access.chai deleted file mode 100644 index a0c6b966..00000000 --- a/unittests/3.x/vector_paren_literal_access.chai +++ /dev/null @@ -1 +0,0 @@ -assert_equal(1, ([1,2,3])[0]) diff --git a/unittests/3.x/vector_push_back.chai b/unittests/3.x/vector_push_back.chai deleted file mode 100644 index 715082bd..00000000 --- a/unittests/3.x/vector_push_back.chai +++ /dev/null @@ -1,5 +0,0 @@ -var x = [1, 2] -x.push_back(3) -assert_equal(3, x.size()) -assert_equal(3, x.back()) -assert_equal(1, x.front()) diff --git a/unittests/3.x/vector_push_empty.chai b/unittests/3.x/vector_push_empty.chai deleted file mode 100644 index 29c568d1..00000000 --- a/unittests/3.x/vector_push_empty.chai +++ /dev/null @@ -1,4 +0,0 @@ -var bob = [] -bob.push_back(3) -assert_equal(1, bob.size()) -assert_equal(3, bob.front()) diff --git a/unittests/3.x/zip.chai b/unittests/3.x/zip.chai deleted file mode 100644 index d39583f2..00000000 --- a/unittests/3.x/zip.chai +++ /dev/null @@ -1,5 +0,0 @@ -var z = zip([1, 2, 3], [4, 5, 6]) - -assert_equal([1,4], z[0]) -assert_equal([2,5], z[1]) -assert_equal([3,6], z[2]) diff --git a/unittests/3.x/zip_with.chai b/unittests/3.x/zip_with.chai deleted file mode 100644 index 1fe3dd90..00000000 --- a/unittests/3.x/zip_with.chai +++ /dev/null @@ -1,3 +0,0 @@ -var z = zip_with(`+`, [1, 2, 3], [4, 5, 6]) - -assert_equal([5,7,9], z) diff --git a/unittests/boxed_cast_test.cpp b/unittests/boxed_cast_test.cpp index 855619a3..2f9261e6 100644 --- a/unittests/boxed_cast_test.cpp +++ b/unittests/boxed_cast_test.cpp @@ -1,15 +1,13 @@ #include - using namespace chaiscript; - template -void use(T){} +void use(T) { +} template -bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) -{ +bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) { try { To ret = chaiscript::boxed_cast(bv); use(ret); @@ -17,9 +15,8 @@ bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) if (expectedpass) { std::cerr << "Failure in run_test_type_conversion: " << e.what() << '\n'; return false; - } else { - return true; } + return true; } catch (const std::exception &e) { std::cerr << "Unexpected standard exception when attempting cast_conversion: " << e.what() << '\n'; return false; @@ -27,41 +24,52 @@ bool run_test_type_conversion(const Boxed_Value &bv, bool expectedpass) std::cerr << "Unexpected unknown exception when attempting cast_conversion.\n"; return false; } - - if (expectedpass) - { - return true; - } else { - return false; - } + + return expectedpass; } template -bool test_type_conversion(const Boxed_Value &bv, bool expectedpass) -{ +bool test_type_conversion(const Boxed_Value &bv, bool expectedpass) { bool ret = run_test_type_conversion(bv, expectedpass); - if (!ret) - { - std::cerr << "Error with type conversion test. From: " - << (bv.is_const()?(std::string("const ")):(std::string())) << bv.get_type_info().name() - << " To: " - << (std::is_const::value?(std::string("const ")):(std::string())) << typeid(To).name() - << " test was expected to " << ((expectedpass)?(std::string("succeed")):(std::string("fail"))) << " but did not\n"; + if (!ret) { + std::cerr << "Error with type conversion test. From: " << (bv.is_const() ? "const " : "") + << bv.get_type_info().name() << " To: " << (std::is_const::value ? "const " : "") + << typeid(To).name() << " test was expected to " << (expectedpass ? "succeed" : "fail") + << " but did not\n"; } return ret; } template -bool do_test(const Boxed_Value &bv, - bool T, bool ConstT, bool TRef, bool ConstTRef, bool TPtr, - bool ConstTPtr, bool TPtrConst, bool ConstTPtrConst, bool SharedPtrT, bool SharedConstPtrT, - bool SharedPtrTRef, bool ConstSharedPtrT, bool ConstSharedConstPtrT, bool ConstSharedPtrTRef, bool ConstSharedPtrTConstRef, - bool WrappedRef, bool WrappedConstRef, bool ConstWrappedRef, bool ConstWrappedConstRef, bool ConstWrappedRefRef, - bool ConstWrappedConstRefRef, bool Number, bool ConstNumber, bool ConstNumberRef, bool TPtrConstRef, - bool ConstTPtrConstRef) -{ +bool do_test(const Boxed_Value &bv, + bool T, + bool ConstT, + bool TRef, + bool ConstTRef, + bool TPtr, + bool ConstTPtr, + bool TPtrConst, + bool ConstTPtrConst, + bool SharedPtrT, + bool SharedConstPtrT, + bool SharedPtrTRef, + bool ConstSharedPtrT, + bool ConstSharedConstPtrT, + bool ConstSharedPtrTRef, + bool ConstSharedPtrTConstRef, + bool WrappedRef, + bool WrappedConstRef, + bool ConstWrappedRef, + bool ConstWrappedConstRef, + bool ConstWrappedRefRef, + bool ConstWrappedConstRefRef, + bool Number, + bool ConstNumber, + bool ConstNumberRef, + bool TPtrConstRef, + bool ConstTPtrConstRef) { bool passed = true; passed &= test_type_conversion(bv, T); passed &= test_type_conversion(bv, ConstT); @@ -69,22 +77,22 @@ bool do_test(const Boxed_Value &bv, passed &= test_type_conversion(bv, ConstTRef); passed &= test_type_conversion(bv, TPtr); passed &= test_type_conversion(bv, ConstTPtr); - passed &= test_type_conversion(bv, TPtrConst); - passed &= test_type_conversion(bv, ConstTPtrConst); - passed &= test_type_conversion >(bv, SharedPtrT); - passed &= test_type_conversion >(bv, SharedConstPtrT); + passed &= test_type_conversion(bv, TPtrConst); + passed &= test_type_conversion(bv, ConstTPtrConst); + passed &= test_type_conversion>(bv, SharedPtrT); + passed &= test_type_conversion>(bv, SharedConstPtrT); passed &= test_type_conversion &>(bv, SharedPtrTRef); - //passed &= test_type_conversion &>(bv, false); - passed &= test_type_conversion >(bv, ConstSharedPtrT); - passed &= test_type_conversion >(bv, ConstSharedConstPtrT); + // passed &= test_type_conversion &>(bv, false); + passed &= test_type_conversion>(bv, ConstSharedPtrT); + passed &= test_type_conversion>(bv, ConstSharedConstPtrT); passed &= test_type_conversion &>(bv, ConstSharedPtrTRef); passed &= test_type_conversion &>(bv, ConstSharedPtrTConstRef); - passed &= test_type_conversion >(bv, WrappedRef); - passed &= test_type_conversion >(bv, WrappedConstRef); + passed &= test_type_conversion>(bv, WrappedRef); + passed &= test_type_conversion>(bv, WrappedConstRef); passed &= test_type_conversion &>(bv, false); passed &= test_type_conversion &>(bv, false); - passed &= test_type_conversion >(bv, ConstWrappedRef); - passed &= test_type_conversion >(bv, ConstWrappedConstRef); + passed &= test_type_conversion>(bv, ConstWrappedRef); + passed &= test_type_conversion>(bv, ConstWrappedConstRef); passed &= test_type_conversion &>(bv, ConstWrappedRefRef); passed &= test_type_conversion &>(bv, ConstWrappedConstRefRef); passed &= test_type_conversion(bv, Number); @@ -93,12 +101,12 @@ bool do_test(const Boxed_Value &bv, passed &= test_type_conversion(bv, ConstNumberRef); passed &= test_type_conversion(bv, false); passed &= test_type_conversion(bv, false); - passed &= test_type_conversion(bv, false); + passed &= test_type_conversion(bv, false); passed &= test_type_conversion(bv, false); passed &= test_type_conversion(bv, false); passed &= test_type_conversion(bv, false); - passed &= test_type_conversion(bv, TPtrConstRef); - passed &= test_type_conversion(bv, ConstTPtrConstRef); + passed &= test_type_conversion(bv, TPtrConstRef); + passed &= test_type_conversion(bv, ConstTPtrConstRef); passed &= test_type_conversion(bv, true); passed &= test_type_conversion(bv, true); passed &= test_type_conversion(bv, true); @@ -108,154 +116,524 @@ bool do_test(const Boxed_Value &bv, /** Tests intended for built int types **/ template -bool built_in_type_test(const T &initial, bool ispod) -{ +bool built_in_type_test(const T &initial, bool ispod) { bool passed = true; /** value tests **/ T i = T(initial); - passed &= do_test(var(i), true, true, true, true, true, - true, true, true, true, true, - true, true, true, true, true, true, - true, true, true, true, true, - ispod, ispod, ispod, true, true); + passed &= do_test(var(i), + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + ispod, + ispod, + ispod, + true, + true); - passed &= do_test(const_var(i), true, true, false, true, false, - true, false, true, false, true, - false, false, true, false, true, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(i), + true, + true, + false, + true, + false, + true, + false, + true, + false, + true, + false, + false, + true, + false, + true, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); - passed &= do_test(var(&i), true, true, true, true, true, - true, true, true, false, false, - false, false, false, false, false, true, - true, true, true, true, true, - ispod, ispod, ispod, true, true); + passed &= do_test(var(&i), + true, + true, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + true, + true, + ispod, + ispod, + ispod, + true, + true); - passed &= do_test(const_var(&i), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(&i), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); - passed &= do_test(var(std::ref(i)), true, true, true, true, true, - true, true, true, false, false, - false, false, false, false, false, true, - true, true, true, true, true, - ispod, ispod, ispod, true, true); + passed &= do_test(var(std::ref(i)), + true, + true, + true, + true, + true, + true, + true, + true, + false, + false, + false, + false, + false, + false, + false, + true, + true, + true, + true, + true, + true, + ispod, + ispod, + ispod, + true, + true); - passed &= do_test(var(std::cref(i)), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(var(std::cref(i)), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); /** Const Reference Variable tests */ // This reference will be copied on input, which is expected const T &ir = i; - passed &= do_test(var(i), true, true, true, true, true, - true, true, true, true, true, - true, true, true, true, true, true, - true, true, true, true, true, - ispod, ispod, ispod, true, true); + passed &= do_test(var(i), + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + ispod, + ispod, + ispod, + true, + true); // But a pointer or reference to it should be necessarily const - passed &= do_test(var(&ir), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(var(&ir), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); - passed &= do_test(var(std::ref(ir)), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(var(std::ref(ir)), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); // Make sure const of const works too - passed &= do_test(const_var(&ir), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(&ir), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); - passed &= do_test(const_var(std::ref(ir)), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(std::ref(ir)), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); /** Const Reference Variable tests */ // This will always be seen as a const - const T*cip = &i; - passed &= do_test(var(cip), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + const T *cip = &i; + passed &= do_test(var(cip), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); // make sure const of const works - passed &= do_test(const_var(cip), true, true, false, true, false, - true, false, true, false, false, - false, false, false, false, false, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(cip), + true, + true, + false, + true, + false, + true, + false, + true, + false, + false, + false, + false, + false, + false, + false, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); /** shared_ptr tests **/ auto ip = std::make_shared(initial); - passed &= do_test(var(ip), true, true, true, true, true, - true, true, true, true, true, - true, true, true, true, true, true, - true, true, true, true, true, - ispod, ispod, ispod, true, true); + passed &= do_test(var(ip), + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true, + ispod, + ispod, + ispod, + true, + true); - passed &= do_test(const_var(ip), true, true, false, true, false, - true, false, true, false, true, - false, false, true, false, true, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(const_var(ip), + true, + true, + false, + true, + false, + true, + false, + true, + false, + true, + false, + false, + true, + false, + true, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); /** const shared_ptr tests **/ auto ipc = std::make_shared(T(initial)); - passed &= do_test(var(ipc), true, true, false, true, false, - true, false, true, false, true, - false, false, true, false, true, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); + passed &= do_test(var(ipc), + true, + true, + false, + true, + false, + true, + false, + true, + false, + true, + false, + false, + true, + false, + true, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); // const of this should be the same, making sure it compiles - passed &= do_test(const_var(ipc), true, true, false, true, false, - true, false, true, false, true, - false, false, true, false, true, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); - + passed &= do_test(const_var(ipc), + true, + true, + false, + true, + false, + true, + false, + true, + false, + true, + false, + false, + true, + false, + true, + false, + true, + false, + true, + false, + true, + ispod, + ispod, + ispod, + false, + true); /** Double ptr tests **/ /* - T **doublep; + T **doublep; - passed &= do_test(var(doublep), true, true, false, true, false, - true, false, true, false, true, - false, true, false, true, false, - true, false, true, false, true, - ispod, ispod, ispod, false, true); -*/ + passed &= do_test(var(doublep), true, true, false, true, false, + true, false, true, false, true, + false, true, false, true, false, + true, false, true, false, true, + ispod, ispod, ispod, false, true); + */ return passed; } - template -bool pointer_test(const T& default_value, const T& new_value) -{ +bool pointer_test(const T &default_value, const T &new_value) { T *p = new T(default_value); // we store a pointer to a pointer, so we can get a pointer to a pointer @@ -263,14 +641,13 @@ bool pointer_test(const T& default_value, const T& new_value) T **result = boxed_cast(var(&p)); *(*result) = new_value; - - if (p != (*result) ) { + if (p != (*result)) { std::cerr << "Pointer passed in different than one returned\n"; delete p; return false; } - if (*p != *(*result) ) { + if (*p != *(*result)) { std::cerr << "Somehow dereferenced pointer values are not the same?\n"; delete p; return false; @@ -287,22 +664,18 @@ bool pointer_test(const T& default_value, const T& new_value) delete p; return false; } - - } - -int main() -{ +int main() { bool passed = true; /* - bool T, bool ConstT, bool TRef, bool ConstTRef, bool TPtr, - bool ConstTPtr, bool TPtrConst, bool ConstTPtrConst, bool SharedPtrT, bool SharedConstPtrT, - bool ConstSharedPtrT, bool ConstSharedConstPtrT, bool ConstSharedPtrTRef, bool ConstSharedPtrTConstRef, bool WrappedRef, - bool WrappedConstRef, bool ConstWrappedRef, bool ConstWrappedConstRef, bool ConstWrappedRefRef, bool ConstWrappedConstRefRef, - bool Number, bool ConstNumber, bool ConstNumberRef - */ + bool T, bool ConstT, bool TRef, bool ConstTRef, bool TPtr, + bool ConstTPtr, bool TPtrConst, bool ConstTPtrConst, bool SharedPtrT, bool SharedConstPtrT, + bool ConstSharedPtrT, bool ConstSharedConstPtrT, bool ConstSharedPtrTRef, bool ConstSharedPtrTConstRef, bool WrappedRef, + bool WrappedConstRef, bool ConstWrappedRef, bool ConstWrappedConstRef, bool ConstWrappedRefRef, bool ConstWrappedConstRefRef, + bool Number, bool ConstNumber, bool ConstNumberRef + */ passed &= built_in_type_test(5, true); passed &= built_in_type_test(1.1, true); @@ -311,15 +684,13 @@ int main() passed &= built_in_type_test('a', true); passed &= built_in_type_test(false, false); passed &= built_in_type_test("Hello World", false); - - // storing a pointer + + // storing a pointer passed &= pointer_test(1, 0); - if (passed) - { + if (passed) { return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; } + return EXIT_FAILURE; } diff --git a/unittests/c_linkage_test.cpp b/unittests/c_linkage_test.cpp index 180686af..f4ef9f77 100644 --- a/unittests/c_linkage_test.cpp +++ b/unittests/c_linkage_test.cpp @@ -1,21 +1,16 @@ -#include #include "../static_libs/chaiscript_parser.hpp" #include "../static_libs/chaiscript_stdlib.hpp" +#include - -extern "C" -{ - int do_something(int i) - { - return i % 2; - } +extern "C" { +int do_something(int i) { + return i % 2; +} } -int main() -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +int main() { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&do_something), "do_something"); - return chai.eval("do_something(101)") == 101 % 2?EXIT_SUCCESS:EXIT_FAILURE; - + return chai.eval("do_something(101)") == 101 % 2 ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/unittests/catch.hpp b/unittests/catch.hpp index ecd8907e..2e78ece8 100644 --- a/unittests/catch.hpp +++ b/unittests/catch.hpp @@ -1,9 +1,9 @@ /* - * Catch v2.2.2 - * Generated: 2018-04-06 12:05:03.186665 + * Catch v2.13.6 + * Generated: 2021-04-16 18:23:38.044268 * ---------------------------------------------------------- * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2018 Two Blue Cubes Ltd. All rights reserved. + * Copyright (c) 2021 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) @@ -12,83 +12,89 @@ #define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED // start catch.hpp - #define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 2 -#define CATCH_VERSION_PATCH 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 6 #ifdef __clang__ -# pragma clang system_header +#pragma clang system_header #elif defined __GNUC__ -# pragma GCC system_header +#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 ignored "-Wunused-variable" -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif +#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__ -# pragma GCC diagnostic ignored "-Wparentheses" -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" +// 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 +#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 -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#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 TARGET_OS_OSX == 1 -# define CATCH_PLATFORM_MAC -# elif TARGET_OS_IPHONE == 1 -# define CATCH_PLATFORM_IPHONE -# endif +#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 +#define CATCH_PLATFORM_LINUX -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) -# define CATCH_PLATFORM_WINDOWS +#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 +#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(); + unsigned int rngSeed(); } // end catch_user_interfaces.h @@ -104,6 +110,7 @@ namespace Catch { // 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 @@ -116,52 +123,94 @@ namespace Catch { #ifdef __cplusplus -# if __cplusplus >= 201402L -# define CATCH_CPP14_OR_GREATER -# endif +#if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +#define CATCH_CPP14_OR_GREATER +#endif -# if __cplusplus >= 201703L -# define CATCH_CPP17_OR_GREATER -# endif +#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#define CATCH_CPP17_OR_GREATER +#endif #endif -#if defined(CATCH_CPP17_OR_GREATER) -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +// 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 -#ifdef __clang__ +#if defined(__clang__) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") -# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic pop" ) +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma("clang diagnostic push") +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma("clang diagnostic pop") -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic push" ) \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - _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 +#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 +#define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS #endif #ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE +#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 //////////////////////////////////////////////////////////////////////////////// @@ -170,35 +219,65 @@ namespace Catch { // 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 +#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++ -#ifdef _MSC_VER +#if defined(_MSC_VER) -# if _MSC_VER >= 1900 // Visual Studio 2015 or newer -# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS -# endif +#define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma(warning(push)) +#define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma(warning(pop)) // 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(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +#define CATCH_CONFIG_COLOUR_NONE +#else +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +#endif + +// 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(__clang__) // Handle Clang masquerading for msvc +#if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +#define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif // MSVC_TRADITIONAL +#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 +#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 @@ -206,121 +285,279 @@ namespace Catch { // 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 +#if (!defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L) +#define CATCH_INTERNAL_CONFIG_COUNTER #endif -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER +//////////////////////////////////////////////////////////////////////////////// + +// 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(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH + +#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 __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 +#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 +#define CATCH_CONFIG_WCHAR #endif -#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) -# define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#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 -# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS +#define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS #endif #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -# define CATCH_INTERNAL_UNSUPPRESS_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 ) +#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__ ) +#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__ ) +#define INTERNAL_CATCH_UNIQUE_NAME(name) INTERNAL_CATCH_UNIQUE_NAME_LINE(name, __LINE__) #endif +#include #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 CaseSensitive { + enum Choice { + Yes, + No }; + }; - struct SourceLineInfo { + class NonCopyable { + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo( SourceLineInfo && ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo& operator = ( SourceLineInfo && ) = default; - - bool empty() const noexcept; - 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 ); - - // 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; + struct SourceLineInfo { + SourceLineInfo() = delete; + SourceLineInfo(char const *_file, std::size_t _line) noexcept + : file(_file) + , line(_line) { } -} -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __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; + } +} // namespace Catch + +#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 ); - }; + struct RegistrarForTagAliases { + RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo); + }; } // end namespace Catch -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#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 @@ -328,239 +565,1252 @@ namespace Catch { // start catch_interfaces_testcase.h #include -#include namespace Catch { + class TestSpec; - class TestSpec; + struct ITestInvoker { + virtual void invoke() const = 0; + virtual ~ITestInvoker(); + }; - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; + class TestCase; + struct IConfig; - using ITestCasePtr = std::shared_ptr; + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const &getAllTests() const = 0; + virtual std::vector const &getAllTestsSorted(IConfig const &config) const = 0; + }; - class TestCase; - struct IConfig; + 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); - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - 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 ); - -} +} // namespace Catch // end catch_interfaces_testcase.h // start catch_stringref.h +#include #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 *; - class StringData; + private: + static constexpr char const *const s_empty = ""; - /// 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. c_str() must return a null terminated - /// string, however, and so the StringRef will internally take ownership - /// (taking a copy), if necessary. In theory this ownership is not externally - /// visible - but it does mean (substring) StringRefs should not be shared between - /// threads. - class StringRef { - public: - using size_type = std::size_t; + char const *m_start = s_empty; + size_type m_size = 0; - private: - friend struct StringRefTestAccess; + public: // construction + constexpr StringRef() noexcept = default; - char const* m_start; - size_type m_size; + StringRef(char const *rawChars) noexcept; - char* m_data = nullptr; - - void takeOwnership(); - - static constexpr char const* const s_empty = ""; - - public: // construction/ assignment - StringRef() noexcept - : StringRef( s_empty, 0 ) - {} - - StringRef( StringRef const& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ) - {} - - StringRef( StringRef&& other ) noexcept - : m_start( other.m_start ), - m_size( other.m_size ), - m_data( other.m_data ) - { - other.m_data = nullptr; - } - - StringRef( char const* rawChars ) noexcept; - - 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() ) - {} - - ~StringRef() noexcept { - delete[] m_data; - } - - auto operator = ( StringRef const &other ) noexcept -> StringRef& { - delete[] m_data; - m_data = nullptr; - m_start = other.m_start; - m_size = other.m_size; - return *this; - } - - operator std::string() const; - - void swap( StringRef& other ) noexcept; - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != ( StringRef const& other ) const noexcept -> bool; - - auto operator[] ( size_type index ) const noexcept -> char; - - public: // named queries - auto empty() const noexcept -> bool { - return m_size == 0; - } - auto size() const noexcept -> size_type { - return m_size; - } - - auto numberOfCharacters() const noexcept -> size_type; - auto c_str() const -> char const*; - - public: // substrings and searches - auto substr( size_type start, size_type size ) const noexcept -> StringRef; - - // Returns the current start pointer. - // Note that the pointer can change when if the StringRef is a substring - auto currentData() const noexcept -> char const*; - - private: // ownership queries - may not be consistent between calls - auto isOwned() const noexcept -> bool; - auto isSubstring() const noexcept -> bool; - }; - - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string; - auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string; - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); + 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 class L1, typename... E1, template class L2, typename... E2, typename... Rest> \ + struct append, L2, Rest...> { \ + using type = typename append, Rest...>::type; \ + }; \ + template class L1, typename... E1, typename... Rest> \ + struct append, TypeList, Rest...> { \ + using type = L1; \ + }; \ + \ + template class Container, template class List, typename... elems> \ + struct rewrap, List> { \ + using type = TypeList>; \ + }; \ + template class Container, template class List, class... Elems, typename... Elements> \ + struct rewrap, List, Elements...> { \ + using type = typename append>, typename rewrap, Elements...>::type>::type; \ + }; \ + \ + template class Final, template class... Containers, typename... Types> \ + struct create, TypeList> { \ + using type = typename append, typename rewrap, Types...>::type...>::type; \ + }; \ + template class Final, template class List, typename... Ts> \ + struct convert> { \ + using type = typename append, TypeList...>::type; \ + }; + +#define INTERNAL_CATCH_NTTP_1(signature, ...) \ + template \ + struct Nttp { \ + }; \ + template \ + constexpr auto get_wrapper() noexcept->Nttp<__VA_ARGS__> { \ + return {}; \ + } \ + template class...> \ + struct NttpTemplateTypeList { \ + }; \ + template class... Cs> \ + constexpr auto get_wrapper() noexcept->NttpTemplateTypeList { \ + return {}; \ + } \ + \ + template class Container, \ + template \ + class List, \ + INTERNAL_CATCH_REMOVE_PARENS(signature)> \ + struct rewrap, List<__VA_ARGS__>> { \ + using type = TypeList>; \ + }; \ + template class Container, \ + template \ + class List, \ + INTERNAL_CATCH_REMOVE_PARENS(signature), \ + typename... Elements> \ + struct rewrap, List<__VA_ARGS__>, Elements...> { \ + using type = \ + typename append>, typename rewrap, Elements...>::type>::type; \ + }; \ + template class Final, template class... Containers, typename... Types> \ + struct create, TypeList> { \ + using type = typename append, typename rewrap, Types...>::type...>::type; \ + }; + +#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature) \ + template \ + static void TestName() +#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...) \ + template \ + static void TestName() + +#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature) \ + template \ + static void TestName() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature, ...) \ + template \ + static void TestName() + +#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature) \ + template \ + void reg_test(TypeList, Catch::NameAndTags nameAndTags) { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestFunc), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...) \ + template \ + void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags) { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...) \ + template \ + void reg_test(TypeList, Catch::StringRef className, Catch::NameAndTags nameAndTags) { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestName::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags); \ + } + +#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...) \ + template \ + void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags) { \ + Catch::AutoReg(Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags); \ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature) \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + } + +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...) \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \ + void test(); \ + } + +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature) \ + template \ + void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName::test() +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...) \ + template \ + void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_NTTP_0 +#define INTERNAL_CATCH_NTTP_GEN(...) \ + INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_1(__VA_ARGS__), \ + INTERNAL_CATCH_NTTP_0) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0) \ + (TestName, ClassName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER0, \ + INTERNAL_CATCH_NTTP_REGISTER0) \ + (TestFunc, __VA_ARGS__) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST0) \ + (TestName, __VA_ARGS__) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) \ + INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_REMOVE_PARENS_11_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_10_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_9_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_8_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_7_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_6_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_5_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_4_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_3_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_2_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_1_ARG) \ + (__VA_ARGS__) +#else +#define INTERNAL_CATCH_NTTP_0(signature) +#define INTERNAL_CATCH_NTTP_GEN(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_1, \ + INTERNAL_CATCH_NTTP_0)(__VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0, \ + INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER, \ + INTERNAL_CATCH_NTTP_REGISTER0, \ + INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)) +#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST1, \ + INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL("dummy", \ + __VA_ARGS__, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DEFINE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST_X, \ + INTERNAL_CATCH_DECLARE_SIG_TEST1, \ + INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)) +#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, \ + INTERNAL_CATCH_REMOVE_PARENS_11_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_10_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_9_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_8_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_7_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_6_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_5_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_4_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_3_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_2_ARG, \ + INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)) +#endif + +// end catch_preprocessor.hpp +// start catch_meta.hpp + +#include + +namespace Catch { + template + struct always_false : std::false_type { + }; + + template + struct true_given : std::true_type { + }; + struct is_callable_tester { + template + true_given()(std::declval()...))> static test(int); + template + std::false_type static test(...); + }; + + template + struct is_callable; + + template + struct is_callable : decltype(is_callable_tester::test(0)) { + }; + +#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703 + // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is + // replaced with std::invoke_result here. + template + using FunctionReturnType = std::remove_reference_t>>; +#else + // Keep ::type here because we still support C++11 + template + using FunctionReturnType = typename std::remove_reference::type>::type>::type; +#endif + } // namespace Catch -// end catch_stringref.h -namespace Catch { - -template -class TestInvokerAsMethod : public ITestInvoker { - void (C::*m_testAsMethod)(); -public: - TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {} - - void invoke() const override { - C obj; - (obj.*m_testAsMethod)(); - } -}; - -auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*; - -template -auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* { - return new(std::nothrow) TestInvokerAsMethod( testAsMethod ); +namespace mpl_ { + struct na; } -struct NameAndTags { - NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept; +// end catch_meta.hpp +namespace Catch { + template + class TestInvokerAsMethod : public ITestInvoker { + void (C::*m_testAsMethod)(); + + public: + TestInvokerAsMethod(void (C::*testAsMethod)()) noexcept + : m_testAsMethod(testAsMethod) { + } + + void invoke() const override { + C obj; + (obj.*m_testAsMethod)(); + } + }; + + auto makeTestInvoker(void (*testAsFunction)()) noexcept -> ITestInvoker *; + + template + auto makeTestInvoker(void (C::*testAsMethod)()) noexcept -> ITestInvoker * { + return new (std::nothrow) TestInvokerAsMethod(testAsMethod); + } + + struct NameAndTags { + NameAndTags(StringRef const &name_ = StringRef(), StringRef const &tags_ = StringRef()) noexcept; StringRef name; StringRef tags; -}; + }; -struct AutoReg : NonCopyable { - AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept; + struct AutoReg : NonCopyable { + AutoReg(ITestInvoker *invoker, SourceLineInfo const &lineInfo, StringRef const &classOrMethod, NameAndTags const &nameAndTags) noexcept; ~AutoReg(); -}; + }; } // end namespace Catch #if defined(CATCH_CONFIG_DISABLE) - #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \ - namespace{ \ - struct TestName : ClassName { \ - void test(); \ - }; \ - } \ - void TestName::test() +#define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(TestName, ...) static void TestName() +#define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(TestName, ClassName, ...) \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + } \ + void TestName::test() +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2(TestName, TestFunc, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, ...) \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + } \ + } \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename TestType, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename TestType, \ + __VA_ARGS__)) #endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ - static void TestName(); \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - static void TestName() - #define INTERNAL_CATCH_TESTCASE( ... ) \ - INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2(INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__)) +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ \ - struct TestName : ClassName{ \ - void test(); \ - }; \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ - } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - void TestName::test() - #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ - INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2(INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif +#endif - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, "", Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TESTCASE2(TestName, ...) \ + static void TestName(); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(Catch::makeTestInvoker(&TestName), \ + CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), \ + Catch::NameAndTags{__VA_ARGS__}); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static void TestName() +#define INTERNAL_CATCH_TESTCASE(...) INTERNAL_CATCH_TESTCASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_METHOD_AS_TEST_CASE(QualifiedMethod, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(Catch::makeTestInvoker(&QualifiedMethod), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod, \ + Catch::NameAndTags{__VA_ARGS__}); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST_CASE_METHOD2(TestName, ClassName, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME(autoRegistrar)(Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName, \ + Catch::NameAndTags{__VA_ARGS__}); /* NOLINT */ \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + void TestName::test() +#define INTERNAL_CATCH_TEST_CASE_METHOD(ClassName, ...) \ + INTERNAL_CATCH_TEST_CASE_METHOD2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), ClassName, __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_REGISTER_TESTCASE(Function, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( \ + autoRegistrar)(Catch::makeTestInvoker(Function), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{__VA_ARGS__}); /* NOLINT \ + */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template \ + struct TestName { \ + TestName() { \ + int index = 0; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)}; \ + using expander = int[]; \ + (void)expander{(reg_test(Types{}, Catch::NameAndTags{Name " - " + std::string(tmpl_types[index]), Tags}), index++)...}; /* NOLINT \ + */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + TestName(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename TestType, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename TestType, \ + __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + static void TestFuncName(); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template \ + struct TestName { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const *tmpl_types[] \ + = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))}; \ + constexpr char const *types_list[] \ + = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))}; \ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]); \ + (void)expander{(Catch::AutoReg(Catch::makeTestInvoker(&TestFuncName), \ + CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), \ + Catch::NameAndTags{Name " - " + std::string(tmpl_types[index / num_types]) + "<" \ + + std::string(types_list[index % num_types]) + ">", \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + using TestInit = \ + typename create()), \ + TypeList>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + static void TestFuncName() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + static void TestFunc(); \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + template \ + struct TestName { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{(Catch::AutoReg(Catch::makeTestInvoker(&TestFunc), \ + CATCH_INTERNAL_LINEINFO, \ + Catch::StringRef(), \ + Catch::NameAndTags{Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " \ + + std::to_string(index), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + using TestInit = typename convert::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + static void TestFunc() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + Name, \ + Tags, \ + TmplList) + +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, ...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature)); \ + INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template \ + struct TestNameClass { \ + TestNameClass() { \ + int index = 0; \ + constexpr char const *tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)}; \ + using expander = int[]; \ + (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{Name " - " + std::string(tmpl_types[index]), Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + TestNameClass(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) { \ + INTERNAL_CATCH_TYPE_GEN \ + INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature)) \ + template \ + struct TestNameClass { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + constexpr char const *tmpl_types[] \ + = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))}; \ + constexpr char const *types_list[] \ + = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))}; \ + constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]); \ + (void)expander{(Catch::AutoReg(Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName, \ + Catch::NameAndTags{Name " - " + std::string(tmpl_types[index / num_types]) + "<" \ + + std::string(types_list[index % num_types]) + ">", \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + using TestInit = \ + typename create()), \ + TypeList>::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(ClassName, Name, Tags, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + ClassName, \ + Name, \ + Tags, \ + typename T, \ + __VA_ARGS__)) +#endif + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__) +#else +#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(ClassName, Name, Tags, Signature, ...) \ + INTERNAL_CATCH_EXPAND_VARGS( \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME( \ + ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + ClassName, \ + Name, \ + Tags, \ + Signature, \ + __VA_ARGS__)) +#endif + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, TmplList) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + template \ + struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ + void test(); \ + }; \ + namespace { \ + namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \ + INTERNAL_CATCH_TYPE_GEN \ + template \ + struct TestNameClass { \ + void reg_tests() { \ + int index = 0; \ + using expander = int[]; \ + (void)expander{(Catch::AutoReg(Catch::makeTestInvoker(&TestName::test), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName, \ + Catch::NameAndTags{Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " \ + + std::to_string(index), \ + Tags}), \ + index++)...}; /* NOLINT */ \ + } \ + }; \ + static int INTERNAL_CATCH_UNIQUE_NAME(globalRegistrar) = []() { \ + using TestInit = typename convert::type; \ + TestInit t; \ + t.reg_tests(); \ + return 0; \ + }(); \ + } \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + template \ + void TestName::test() + +#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \ + INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____), \ + INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____), \ + ClassName, \ + Name, \ + Tags, \ + TmplList) // end catch_test_registry.h // start catch_capture.hpp @@ -572,62 +1822,65 @@ struct AutoReg : NonCopyable { // start catch_result_type.h namespace Catch { + // ResultWas::OfType enum + struct ResultWas { + enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, - // ResultWas::OfType enum - struct ResultWas { enum OfType { - Unknown = -1, - Ok = 0, - Info = 1, - Warning = 2, + FailureBit = 0x10, - FailureBit = 0x10, + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, - ExpressionFailed = FailureBit | 1, - ExplicitFailure = FailureBit | 2, + Exception = 0x100 | FailureBit, - Exception = 0x100 | FailureBit, + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, - ThrewException = Exception | 1, - DidntThrowException = Exception | 2, + FatalErrorCondition = 0x200 | FailureBit - FatalErrorCondition = 0x200 | FailureBit + }; + }; - }; }; + bool isOk(ResultWas::OfType resultType); + bool isJustInfo(int flags); - bool isOk( ResultWas::OfType resultType ); - bool isJustInfo( int flags ); + // ResultDisposition::Flags enum + struct ResultDisposition { + enum Flags { + Normal = 0x01, - // ResultDisposition::Flags enum - struct ResultDisposition { enum Flags { - Normal = 0x01, + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; + }; - ContinueOnFailure = 0x02, // Failures fail test, but execution continues - FalseTest = 0x04, // Prefix expression with ! - SuppressFail = 0x08 // Failures are reported but do not fail the test - }; }; + ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs); - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ); - - bool shouldContinueOnFailure( int flags ); - inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } - bool shouldSuppressFailure( int flags ); + bool shouldContinueOnFailure(int flags); + inline bool isFalseTest(int flags) { + return (flags & ResultDisposition::FalseTest) != 0; + } + bool shouldSuppressFailure(int flags); } // end namespace Catch // end catch_result_type.h namespace Catch { + struct AssertionInfo { + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; - struct AssertionInfo - { - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; - - // We want to delete this constructor but a compiler bug in 4.8 means - // the struct is then treated as non-aggregate - //AssertionInfo() = delete; - }; + // We want to delete this constructor but a compiler bug in 4.8 means + // the struct is then treated as non-aggregate + // AssertionInfo() = delete; + }; } // end namespace Catch @@ -636,52 +1889,89 @@ namespace Catch { // start catch_tostring.h -#include #include -#include #include +#include +#include // start catch_stream.h -#include #include +#include #include namespace Catch { + std::ostream &cout(); + std::ostream &cerr(); + std::ostream &clog(); - std::ostream& cout(); - std::ostream& cerr(); - std::ostream& clog(); + class StringRef; - class StringRef; + struct IStream { + virtual ~IStream(); + virtual std::ostream &stream() const = 0; + }; - struct IStream { - virtual ~IStream(); - virtual std::ostream& stream() const = 0; - }; + auto makeStream(StringRef const &filename) -> IStream const *; - auto makeStream( StringRef const &filename ) -> IStream const*; + class ReusableStringStream : NonCopyable { + std::size_t m_index; + std::ostream *m_oss; - class ReusableStringStream { - std::size_t m_index; - std::ostream* m_oss; - public: - ReusableStringStream(); - ~ReusableStringStream(); + public: + ReusableStringStream(); + ~ReusableStringStream(); - auto str() const -> std::string; + auto str() const -> std::string; - template - auto operator << ( T const& value ) -> ReusableStringStream& { - *m_oss << value; - return *this; - } - auto get() -> std::ostream& { return *m_oss; } - - static void cleanup(); - }; -} + template + auto operator<<(T const &value) -> ReusableStringStream & { + *m_oss << value; + return *this; + } + auto get() -> std::ostream & { return *m_oss; } + }; +} // namespace Catch // end catch_stream.h +// start catch_interfaces_enum_values_registry.h + +#include + +namespace Catch { + namespace Detail { + struct EnumInfo { + StringRef m_name; + std::vector> m_values; + + ~EnumInfo(); + + StringRef lookup(int value) const; + }; + } // namespace Detail + + struct IMutableEnumValuesRegistry { + virtual ~IMutableEnumValuesRegistry(); + + virtual Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::vector const &values) = 0; + + template + Detail::EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::initializer_list values) { + static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int"); + std::vector intValues; + intValues.reserve(values.size()); + for (auto enumValue : values) + intValues.push_back(static_cast(enumValue)); + return registerEnum(enumName, allEnums, intValues); + } + }; + +} // namespace Catch + +// end catch_interfaces_enum_values_registry.h + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW +#include +#endif #ifdef __OBJC__ // start catch_objc_arc.hpp @@ -694,33 +1984,34 @@ namespace Catch { #define CATCH_ARC_ENABLED 0 #endif -void arcSafeRelease( NSObject* obj ); -id performOptionalSelector( id obj, SEL sel ); +void arcSafeRelease(NSObject *obj); +id performOptionalSelector(id obj, SEL sel); #if !CATCH_ARC_ENABLED -inline void arcSafeRelease( NSObject* obj ) { - [obj release]; +inline void arcSafeRelease(NSObject *obj) { + [obj release]; } -inline id performOptionalSelector( id obj, SEL sel ) { - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; - return nil; +inline id performOptionalSelector(id obj, SEL sel) { + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; + return nil; } #define CATCH_UNSAFE_UNRETAINED #define CATCH_ARC_STRONG #else -inline void arcSafeRelease( NSObject* ){} -inline id performOptionalSelector( id obj, SEL sel ) { +inline void arcSafeRelease(NSObject *) { +} +inline id performOptionalSelector(id obj, SEL sel) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" #endif - if( [obj respondsToSelector: sel] ) - return [obj performSelector: sel]; + if ([obj respondsToSelector:sel]) + return [obj performSelector:sel]; #ifdef __clang__ #pragma clang diagnostic pop #endif - return nil; + return nil; } #define CATCH_UNSAFE_UNRETAINED __unsafe_unretained #define CATCH_ARC_STRONG __strong @@ -731,295 +2022,302 @@ inline id performOptionalSelector( id obj, SEL sel ) { #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless +#pragma warning(disable : 4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless #endif -// 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 { - // Bring in operator<< from global namespace into Catch namespace - using ::operator<<; + namespace Detail { + extern const std::string unprintableString; - namespace Detail { + std::string rawMemoryToString(const void *object, std::size_t size); - extern const std::string unprintableString; - - std::string rawMemoryToString( const void *object, std::size_t size ); - - template - std::string rawMemoryToString( const T& object ) { - return rawMemoryToString( &object, sizeof(object) ); - } - - template - class IsStreamInsertable { - template - static auto test(int) - -> decltype(std::declval() << std::declval(), std::true_type()); - - template - static auto test(...)->std::false_type; - - public: - static const bool value = decltype(test(0))::value; - }; - - template - std::string convertUnknownEnumToString( E e ); - - template - typename std::enable_if::value, std::string>::type convertUnstreamable( T const& value ) { -#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) - (void)value; - return Detail::unprintableString; -#else - return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); -#endif - } - template - typename std::enable_if::value, std::string>::type convertUnstreamable( T const& value ) { - return convertUnknownEnumToString( value ); - } - -#if defined(_MANAGED) - //! Convert a CLR string to a utf8 std::string - template - std::string clrReferenceToString( T^ ref ) { - if (ref == nullptr) - return std::string("null"); - auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); - cli::pin_ptr p = &bytes[0]; - return std::string(reinterpret_cast(p), bytes->Length); - } -#endif - - } // namespace Detail - - // If we decide for C++14, change these to enable_if_ts - template - struct StringMaker { - template - static - typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type - convert(const Fake& value) { - ReusableStringStream rss; - rss << value; - return rss.str(); - } - - template - static - typename std::enable_if::value, std::string>::type - convert( const Fake& value ) { - return Detail::convertUnstreamable( value ); - } - }; - - namespace Detail { - - // This function dispatches all stringification requests inside of Catch. - // Should be preferably called fully qualified, like ::Catch::Detail::stringify - template - std::string stringify(const T& e) { - return ::Catch::StringMaker::type>::type>::convert(e); - } - - template - std::string convertUnknownEnumToString( E e ) { - return ::Catch::Detail::stringify(static_cast::type>(e)); - } - -#if defined(_MANAGED) - template - std::string stringify( T^ e ) { - return ::Catch::StringMaker::convert(e); - } -#endif - - } // namespace Detail - - // Some predefined specializations - - template<> - struct StringMaker { - static std::string convert(const std::string& str); - }; -#ifdef CATCH_CONFIG_WCHAR - template<> - struct StringMaker { - static std::string convert(const std::wstring& wstr); - }; -#endif - - template<> - struct StringMaker { - static std::string convert(char const * str); - }; - template<> - struct StringMaker { - static std::string convert(char * str); - }; - -#ifdef CATCH_CONFIG_WCHAR - template<> - struct StringMaker { - static std::string convert(wchar_t const * str); - }; - template<> - struct StringMaker { - static std::string convert(wchar_t * str); - }; -#endif - - // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, - // while keeping string semantics? - template - struct StringMaker { - static std::string convert(char const* str) { - return ::Catch::Detail::stringify(std::string{ str }); - } - }; - template - struct StringMaker { - static std::string convert(signed char const* str) { - return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); - } - }; - template - struct StringMaker { - static std::string convert(unsigned char const* str) { - return ::Catch::Detail::stringify(std::string{ reinterpret_cast(str) }); - } - }; - - template<> - struct StringMaker { - static std::string convert(int value); - }; - template<> - struct StringMaker { - static std::string convert(long value); - }; - template<> - struct StringMaker { - static std::string convert(long long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned int value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long value); - }; - template<> - struct StringMaker { - static std::string convert(unsigned long long value); - }; - - template<> - struct StringMaker { - static std::string convert(bool b); - }; - - template<> - struct StringMaker { - static std::string convert(char c); - }; - template<> - struct StringMaker { - static std::string convert(signed char c); - }; - template<> - struct StringMaker { - static std::string convert(unsigned char c); - }; - - template<> - struct StringMaker { - static std::string convert(std::nullptr_t); - }; - - template<> - struct StringMaker { - static std::string convert(float value); - }; - template<> - struct StringMaker { - static std::string convert(double value); - }; - - template - struct StringMaker { - template - static std::string convert(U* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - - template - struct StringMaker { - static std::string convert(R C::* p) { - if (p) { - return ::Catch::Detail::rawMemoryToString(p); - } else { - return "nullptr"; - } - } - }; - -#if defined(_MANAGED) - template - struct StringMaker { - static std::string convert( T^ ref ) { - return ::Catch::Detail::clrReferenceToString(ref); - } - }; -#endif - - namespace Detail { - template - std::string rangeToString(InputIterator first, InputIterator last) { - ReusableStringStream rss; - rss << "{ "; - if (first != last) { - rss << ::Catch::Detail::stringify(*first); - for (++first; first != last; ++first) - rss << ", " << ::Catch::Detail::stringify(*first); - } - rss << " }"; - return rss.str(); - } + template + std::string rawMemoryToString(const T &object) { + return rawMemoryToString(&object, sizeof(object)); } + template + class IsStreamInsertable { + template + static auto test(int) -> decltype(std::declval() << std::declval(), std::true_type()); + + template + static auto test(...) -> std::false_type; + + public: + static const bool value = decltype(test(0))::value; + }; + + template + std::string convertUnknownEnumToString(E e); + + template + typename std::enable_if::value && !std::is_base_of::value, std::string>::type convertUnstreamable(T const &) { + return Detail::unprintableString; + } + template + typename std::enable_if::value && std::is_base_of::value, std::string>::type convertUnstreamable(T const &ex) { + return ex.what(); + } + + template + typename std::enable_if::value, std::string>::type convertUnstreamable(T const &value) { + return convertUnknownEnumToString(value); + } + +#if defined(_MANAGED) + //! Convert a CLR string to a utf8 std::string + template + std::string clrReferenceToString(T ^ ref) { + if (ref == nullptr) + return std::string("null"); + auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString()); + cli::pin_ptr p = &bytes[0]; + return std::string(reinterpret_cast(p), bytes->Length); + } +#endif + + } // namespace Detail + + // If we decide for C++14, change these to enable_if_ts + template + struct StringMaker { + template + static typename std::enable_if<::Catch::Detail::IsStreamInsertable::value, std::string>::type convert(const Fake &value) { + ReusableStringStream rss; + // NB: call using the function-like syntax to avoid ambiguity with + // user-defined templated operator<< under clang. + rss.operator<<(value); + return rss.str(); + } + + template + static typename std::enable_if::value, std::string>::type convert(const Fake &value) { +#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER) + return Detail::convertUnstreamable(value); +#else + return CATCH_CONFIG_FALLBACK_STRINGIFIER(value); +#endif + } + }; + + namespace Detail { + // This function dispatches all stringification requests inside of Catch. + // Should be preferably called fully qualified, like ::Catch::Detail::stringify + template + std::string stringify(const T &e) { + return ::Catch::StringMaker::type>::type>::convert(e); + } + + template + std::string convertUnknownEnumToString(E e) { + return ::Catch::Detail::stringify(static_cast::type>(e)); + } + +#if defined(_MANAGED) + template + std::string stringify(T ^ e) { + return ::Catch::StringMaker::convert(e); + } +#endif + + } // namespace Detail + + // Some predefined specializations + + template<> + struct StringMaker { + static std::string convert(const std::string &str); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::string_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(char const *str); + }; + template<> + struct StringMaker { + static std::string convert(char *str); + }; + +#ifdef CATCH_CONFIG_WCHAR + template<> + struct StringMaker { + static std::string convert(const std::wstring &wstr); + }; + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + template<> + struct StringMaker { + static std::string convert(std::wstring_view str); + }; +#endif + + template<> + struct StringMaker { + static std::string convert(wchar_t const *str); + }; + template<> + struct StringMaker { + static std::string convert(wchar_t *str); + }; +#endif + + // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer, + // while keeping string semantics? + template + struct StringMaker { + static std::string convert(char const *str) { return ::Catch::Detail::stringify(std::string{str}); } + }; + template + struct StringMaker { + static std::string convert(signed char const *str) { + return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); + } + }; + template + struct StringMaker { + static std::string convert(unsigned char const *str) { + return ::Catch::Detail::stringify(std::string{reinterpret_cast(str)}); + } + }; + +#if defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(std::byte value); + }; +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + template<> + struct StringMaker { + static std::string convert(int value); + }; + template<> + struct StringMaker { + static std::string convert(long value); + }; + template<> + struct StringMaker { + static std::string convert(long long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned int value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long value); + }; + template<> + struct StringMaker { + static std::string convert(unsigned long long value); + }; + + template<> + struct StringMaker { + static std::string convert(bool b); + }; + + template<> + struct StringMaker { + static std::string convert(char c); + }; + template<> + struct StringMaker { + static std::string convert(signed char c); + }; + template<> + struct StringMaker { + static std::string convert(unsigned char c); + }; + + template<> + struct StringMaker { + static std::string convert(std::nullptr_t); + }; + + template<> + struct StringMaker { + static std::string convert(float value); + static int precision; + }; + + template<> + struct StringMaker { + static std::string convert(double value); + static int precision; + }; + + template + struct StringMaker { + template + static std::string convert(U *p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + + template + struct StringMaker { + static std::string convert(R C::*p) { + if (p) { + return ::Catch::Detail::rawMemoryToString(p); + } else { + return "nullptr"; + } + } + }; + +#if defined(_MANAGED) + template + struct StringMaker { + static std::string convert(T ^ ref) { return ::Catch::Detail::clrReferenceToString(ref); } + }; +#endif + + namespace Detail { + template + std::string rangeToString(InputIterator first, Sentinel last) { + ReusableStringStream rss; + rss << "{ "; + if (first != last) { + rss << ::Catch::Detail::stringify(*first); + for (++first; first != last; ++first) + rss << ", " << ::Catch::Detail::stringify(*first); + } + rss << " }"; + return rss.str(); + } + } // namespace Detail + #ifdef __OBJC__ - template<> - struct StringMaker { - static std::string convert(NSString * nsstring) { - if (!nsstring) - return "nil"; - return std::string("@") + [nsstring UTF8String]; - } - }; - template<> - struct StringMaker { - static std::string convert(NSObject* nsObject) { - return ::Catch::Detail::stringify([nsObject description]); - } + template<> + struct StringMaker { + static std::string convert(NSString *nsstring) { + if (!nsstring) + return "nil"; + return std::string("@") + [nsstring UTF8String]; + } + }; + template<> + struct StringMaker { + static std::string convert(NSObject *nsObject) { return ::Catch::Detail::stringify([nsObject description]); } + }; + namespace Detail { + inline std::string stringify(NSString *nsstring) { + return StringMaker::convert(nsstring); + } - }; - namespace Detail { - inline std::string stringify( NSString* nsstring ) { - return StringMaker::convert( nsstring ); - } - - } // namespace Detail + } // namespace Detail #endif // __OBJC__ } // namespace Catch @@ -1029,250 +2327,294 @@ namespace Catch { // This means that we do not bring in #if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS) -# define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER -# define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +#define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER +#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER #endif // Separate std::pair specialization #if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER) #include namespace Catch { - template - struct StringMaker > { - static std::string convert(const std::pair& pair) { - ReusableStringStream rss; - rss << "{ " - << ::Catch::Detail::stringify(pair.first) - << ", " - << ::Catch::Detail::stringify(pair.second) - << " }"; - return rss.str(); - } - }; -} + template + struct StringMaker> { + static std::string convert(const std::pair &pair) { + ReusableStringStream rss; + rss << "{ " << ::Catch::Detail::stringify(pair.first) << ", " << ::Catch::Detail::stringify(pair.second) << " }"; + return rss.str(); + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER +#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL) +#include +namespace Catch { + template + struct StringMaker> { + static std::string convert(const std::optional &optional) { + ReusableStringStream rss; + if (optional.has_value()) { + rss << ::Catch::Detail::stringify(*optional); + } else { + rss << "{ }"; + } + return rss.str(); + } + }; +} // namespace Catch +#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER + // Separate std::tuple specialization #if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER) #include namespace Catch { - namespace Detail { - template< - typename Tuple, - std::size_t N = 0, - bool = (N < std::tuple_size::value) - > - struct TupleElementPrinter { - static void print(const Tuple& tuple, std::ostream& os) { - os << (N ? ", " : " ") - << ::Catch::Detail::stringify(std::get(tuple)); - TupleElementPrinter::print(tuple, os); - } - }; - - template< - typename Tuple, - std::size_t N - > - struct TupleElementPrinter { - static void print(const Tuple&, std::ostream&) {} - }; - - } - - template - struct StringMaker> { - static std::string convert(const std::tuple& tuple) { - ReusableStringStream rss; - rss << '{'; - Detail::TupleElementPrinter>::print(tuple, rss.get()); - rss << " }"; - return rss.str(); - } + namespace Detail { + template::value)> + struct TupleElementPrinter { + static void print(const Tuple &tuple, std::ostream &os) { + os << (N ? ", " : " ") << ::Catch::Detail::stringify(std::get(tuple)); + TupleElementPrinter::print(tuple, os); + } }; -} + + template + struct TupleElementPrinter { + static void print(const Tuple &, std::ostream &) {} + }; + + } // namespace Detail + + template + struct StringMaker> { + static std::string convert(const std::tuple &tuple) { + ReusableStringStream rss; + rss << '{'; + Detail::TupleElementPrinter>::print(tuple, rss.get()); + rss << " }"; + return rss.str(); + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER +#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT) +#include namespace Catch { - struct not_this_one {}; // Tag type for detecting which begin/ end are being selected + template<> + struct StringMaker { + static std::string convert(const std::monostate &) { return "{ }"; } + }; - // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace - using std::begin; - using std::end; + template + struct StringMaker> { + static std::string convert(const std::variant &variant) { + if (variant.valueless_by_exception()) { + return "{valueless variant}"; + } else { + return std::visit([](const auto &value) { return ::Catch::Detail::stringify(value); }, variant); + } + } + }; +} // namespace Catch +#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER - not_this_one begin( ... ); - not_this_one end( ... ); +namespace Catch { + // Import begin/ end from std here + using std::begin; + using std::end; - template - struct is_range { - static const bool value = - !std::is_same())), not_this_one>::value && - !std::is_same())), not_this_one>::value; + namespace detail { + template + struct void_type { + using type = void; }; + template + struct is_range_impl : std::false_type { + }; + + template + struct is_range_impl()))>::type> : std::true_type { + }; + } // namespace detail + + template + struct is_range : detail::is_range_impl { + }; + #if defined(_MANAGED) // Managed types are never ranges - template - struct is_range { - static const bool value = false; - }; + template + struct is_range { + static const bool value = false; + }; #endif - template - std::string rangeToString( Range const& range ) { - return ::Catch::Detail::rangeToString( begin( range ), end( range ) ); + template + std::string rangeToString(Range const &range) { + return ::Catch::Detail::rangeToString(begin(range), end(range)); + } + + // Handle vector specially + template + std::string rangeToString(std::vector const &v) { + ReusableStringStream rss; + rss << "{ "; + bool first = true; + for (bool b : v) { + if (first) + first = false; + else + rss << ", "; + rss << ::Catch::Detail::stringify(b); } + rss << " }"; + return rss.str(); + } - // Handle vector specially - template - std::string rangeToString( std::vector const& v ) { - ReusableStringStream rss; - rss << "{ "; - bool first = true; - for( bool b : v ) { - if( first ) - first = false; - else - rss << ", "; - rss << ::Catch::Detail::stringify( b ); - } - rss << " }"; - return rss.str(); - } + template + struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { + static std::string convert(R const &range) { return rangeToString(range); } + }; - template - struct StringMaker::value && !::Catch::Detail::IsStreamInsertable::value>::type> { - static std::string convert( R const& range ) { - return rangeToString( range ); - } - }; - - template - struct StringMaker { - static std::string convert(T const(&arr)[SZ]) { - return rangeToString(arr); - } - }; + template + struct StringMaker { + static std::string convert(T const (&arr)[SZ]) { return rangeToString(arr); } + }; } // namespace Catch // Separate std::chrono::duration specialization #if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +#include #include #include -#include namespace Catch { - -template -struct ratio_string { + template + struct ratio_string { static std::string symbol(); -}; + }; -template -std::string ratio_string::symbol() { + template + std::string ratio_string::symbol() { Catch::ReusableStringStream rss; - rss << '[' << Ratio::num << '/' - << Ratio::den << ']'; + rss << '[' << Ratio::num << '/' << Ratio::den << ']'; return rss.str(); -} -template <> -struct ratio_string { + } + template<> + struct ratio_string { static std::string symbol(); -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static std::string symbol(); -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static std::string symbol(); -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static std::string symbol(); -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static std::string symbol(); -}; -template <> -struct ratio_string { + }; + template<> + struct ratio_string { static std::string symbol(); -}; + }; - //////////// - // std::chrono::duration specializations - template - struct StringMaker> { - static std::string convert(std::chrono::duration const& duration) { - ReusableStringStream rss; - rss << duration.count() << ' ' << ratio_string::symbol() << 's'; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " s"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " m"; - return rss.str(); - } - }; - template - struct StringMaker>> { - static std::string convert(std::chrono::duration> const& duration) { - ReusableStringStream rss; - rss << duration.count() << " h"; - return rss.str(); - } - }; + //////////// + // std::chrono::duration specializations + template + struct StringMaker> { + static std::string convert(std::chrono::duration const &duration) { + ReusableStringStream rss; + rss << duration.count() << ' ' << ratio_string::symbol() << 's'; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " s"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " m"; + return rss.str(); + } + }; + template + struct StringMaker>> { + static std::string convert(std::chrono::duration> const &duration) { + ReusableStringStream rss; + rss << duration.count() << " h"; + return rss.str(); + } + }; - //////////// - // std::chrono::time_point specialization - // Generic time_point cannot be specialized, only std::chrono::time_point - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; - } - }; - // std::chrono::time_point specialization - template - struct StringMaker> { - static std::string convert(std::chrono::time_point const& time_point) { - auto converted = std::chrono::system_clock::to_time_t(time_point); + //////////// + // std::chrono::time_point specialization + // Generic time_point cannot be specialized, only std::chrono::time_point + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch"; + } + }; + // std::chrono::time_point specialization + template + struct StringMaker> { + static std::string convert(std::chrono::time_point const &time_point) { + auto converted = std::chrono::system_clock::to_time_t(time_point); #ifdef _MSC_VER - std::tm timeInfo = {}; - gmtime_s(&timeInfo, &converted); + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &converted); #else - std::tm* timeInfo = std::gmtime(&converted); + std::tm *timeInfo = std::gmtime(&converted); #endif - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp); - } - }; -} + return std::string(timeStamp); + } + }; +} // namespace Catch #endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +#define INTERNAL_CATCH_REGISTER_ENUM(enumName, ...) \ + namespace Catch { \ + template<> \ + struct StringMaker { \ + static std::string convert(enumName value) { \ + static const auto &enumInfo \ + = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum(#enumName, #__VA_ARGS__, {__VA_ARGS__}); \ + return static_cast(enumInfo.lookup(static_cast(value))); \ + } \ + }; \ + } + +#define CATCH_REGISTER_ENUM(enumName, ...) INTERNAL_CATCH_REGISTER_ENUM(enumName, __VA_ARGS__) + #ifdef _MSC_VER #pragma warning(pop) #endif @@ -1282,154 +2624,247 @@ struct ratio_string { #ifdef _MSC_VER #pragma warning(push) -#pragma warning(disable:4389) // '==' : signed/unsigned mismatch -#pragma warning(disable:4018) // more "signed/unsigned mismatch" -#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) -#pragma warning(disable:4180) // qualifier applied to function type has no meaning +#pragma warning(disable : 4389) // '==' : signed/unsigned mismatch +#pragma warning(disable : 4018) // more "signed/unsigned mismatch" +#pragma warning(disable : 4312) // Converting int to T* using reinterpret_cast (issue on x64 platform) +#pragma warning(disable : 4180) // qualifier applied to function type has no meaning +#pragma warning(disable : 4800) // Forcing result to true or false #endif namespace Catch { + struct ITransientExpression { + auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } + auto getResult() const -> bool { return m_result; } + virtual void streamReconstructedExpression(std::ostream &os) const = 0; - struct ITransientExpression { - auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } - auto getResult() const -> bool { return m_result; } - virtual void streamReconstructedExpression( std::ostream &os ) const = 0; - - ITransientExpression( bool isBinaryExpression, bool result ) - : m_isBinaryExpression( isBinaryExpression ), - m_result( result ) - {} - - // We don't actually need a virtual destructor, but many static analysers - // complain if it's not here :-( - virtual ~ITransientExpression(); - - bool m_isBinaryExpression; - bool m_result; - - }; - - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ); - - template - class BinaryExpr : public ITransientExpression { - LhsT m_lhs; - StringRef m_op; - RhsT m_rhs; - - void streamReconstructedExpression( std::ostream &os ) const override { - formatReconstructedExpression - ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); - } - - public: - BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : ITransientExpression{ true, comparisonResult }, - m_lhs( lhs ), - m_op( op ), - m_rhs( rhs ) - {} - }; - - template - class UnaryExpr : public ITransientExpression { - LhsT m_lhs; - - void streamReconstructedExpression( std::ostream &os ) const override { - os << Catch::Detail::stringify( m_lhs ); - } - - public: - explicit UnaryExpr( LhsT lhs ) - : ITransientExpression{ false, lhs ? true : false }, - m_lhs( lhs ) - {} - }; - - // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) - template - auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast(lhs == rhs); } - template - auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast( rhs ); } - template - auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - template - auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) == rhs; } - - template - auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast(lhs != rhs); } - template - auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast( rhs ); } - template - auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - template - auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast( lhs ) != rhs; } - - template - class ExprLhs { - LhsT m_lhs; - public: - explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {} - - template - auto operator == ( RhsT const& rhs ) -> BinaryExpr const { - return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs }; - } - auto operator == ( bool rhs ) -> BinaryExpr const { - return { m_lhs == rhs, m_lhs, "==", rhs }; - } - - template - auto operator != ( RhsT const& rhs ) -> BinaryExpr const { - return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs }; - } - auto operator != ( bool rhs ) -> BinaryExpr const { - return { m_lhs != rhs, m_lhs, "!=", rhs }; - } - - template - auto operator > ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs > rhs), m_lhs, ">", rhs }; - } - template - auto operator < ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs < rhs), m_lhs, "<", rhs }; - } - template - auto operator >= ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs >= rhs), m_lhs, ">=", rhs }; - } - template - auto operator <= ( RhsT const& rhs ) -> BinaryExpr const { - return { static_cast(m_lhs <= rhs), m_lhs, "<=", rhs }; - } - - auto makeUnaryExpr() const -> UnaryExpr { - return UnaryExpr{ m_lhs }; - } - }; - - void handleExpression( ITransientExpression const& expr ); - - template - void handleExpression( ExprLhs const& expr ) { - handleExpression( expr.makeUnaryExpr() ); + ITransientExpression(bool isBinaryExpression, bool result) + : m_isBinaryExpression(isBinaryExpression) + , m_result(result) { } - struct Decomposer { - template - auto operator <= ( T const& lhs ) -> ExprLhs { - return ExprLhs{ lhs }; - } + // We don't actually need a virtual destructor, but many static analysers + // complain if it's not here :-( + virtual ~ITransientExpression(); - auto operator <=( bool value ) -> ExprLhs { - return ExprLhs{ value }; - } - }; + bool m_isBinaryExpression; + bool m_result; + }; + + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs); + + template + class BinaryExpr : public ITransientExpression { + LhsT m_lhs; + StringRef m_op; + RhsT m_rhs; + + void streamReconstructedExpression(std::ostream &os) const override { + formatReconstructedExpression(os, Catch::Detail::stringify(m_lhs), m_op, Catch::Detail::stringify(m_rhs)); + } + + public: + BinaryExpr(bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs) + : ITransientExpression{true, comparisonResult} + , m_lhs(lhs) + , m_op(op) + , m_rhs(rhs) { + } + + template + auto operator&&(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator==(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator!=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator>=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator<=(T) const -> BinaryExpr const { + static_assert(always_false::value, + "chained comparisons are not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + }; + + template + class UnaryExpr : public ITransientExpression { + LhsT m_lhs; + + void streamReconstructedExpression(std::ostream &os) const override { os << Catch::Detail::stringify(m_lhs); } + + public: + explicit UnaryExpr(LhsT lhs) + : ITransientExpression{false, static_cast(lhs)} + , m_lhs(lhs) { + } + }; + + // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int) + template + auto compareEqual(LhsT const &lhs, RhsT const &rhs) -> bool { + return static_cast(lhs == rhs); + } + template + auto compareEqual(T *const &lhs, int rhs) -> bool { + return lhs == reinterpret_cast(rhs); + } + template + auto compareEqual(T *const &lhs, long rhs) -> bool { + return lhs == reinterpret_cast(rhs); + } + template + auto compareEqual(int lhs, T *const &rhs) -> bool { + return reinterpret_cast(lhs) == rhs; + } + template + auto compareEqual(long lhs, T *const &rhs) -> bool { + return reinterpret_cast(lhs) == rhs; + } + + template + auto compareNotEqual(LhsT const &lhs, RhsT &&rhs) -> bool { + return static_cast(lhs != rhs); + } + template + auto compareNotEqual(T *const &lhs, int rhs) -> bool { + return lhs != reinterpret_cast(rhs); + } + template + auto compareNotEqual(T *const &lhs, long rhs) -> bool { + return lhs != reinterpret_cast(rhs); + } + template + auto compareNotEqual(int lhs, T *const &rhs) -> bool { + return reinterpret_cast(lhs) != rhs; + } + template + auto compareNotEqual(long lhs, T *const &rhs) -> bool { + return reinterpret_cast(lhs) != rhs; + } + + template + class ExprLhs { + LhsT m_lhs; + + public: + explicit ExprLhs(LhsT lhs) + : m_lhs(lhs) { + } + + template + auto operator==(RhsT const &rhs) -> BinaryExpr const { + return {compareEqual(m_lhs, rhs), m_lhs, "==", rhs}; + } + auto operator==(bool rhs) -> BinaryExpr const { return {m_lhs == rhs, m_lhs, "==", rhs}; } + + template + auto operator!=(RhsT const &rhs) -> BinaryExpr const { + return {compareNotEqual(m_lhs, rhs), m_lhs, "!=", rhs}; + } + auto operator!=(bool rhs) -> BinaryExpr const { return {m_lhs != rhs, m_lhs, "!=", rhs}; } + + template + auto operator>(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs > rhs), m_lhs, ">", rhs}; + } + template + auto operator<(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs < rhs), m_lhs, "<", rhs}; + } + template + auto operator>=(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs >= rhs), m_lhs, ">=", rhs}; + } + template + auto operator<=(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs <= rhs), m_lhs, "<=", rhs}; + } + template + auto operator|(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs | rhs), m_lhs, "|", rhs}; + } + template + auto operator&(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs & rhs), m_lhs, "&", rhs}; + } + template + auto operator^(RhsT const &rhs) -> BinaryExpr const { + return {static_cast(m_lhs ^ rhs), m_lhs, "^", rhs}; + } + + template + auto operator&&(RhsT const &) -> BinaryExpr const { + static_assert(always_false::value, + "operator&& is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + template + auto operator||(RhsT const &) -> BinaryExpr const { + static_assert(always_false::value, + "operator|| is not supported inside assertions, " + "wrap the expression inside parentheses, or decompose it"); + } + + auto makeUnaryExpr() const -> UnaryExpr { return UnaryExpr{m_lhs}; } + }; + + void handleExpression(ITransientExpression const &expr); + + template + void handleExpression(ExprLhs const &expr) { + handleExpression(expr.makeUnaryExpr()); + } + + struct Decomposer { + template + auto operator<=(T const &lhs) -> ExprLhs { + return ExprLhs{lhs}; + } + + auto operator<=(bool value) -> ExprLhs { return ExprLhs{value}; } + }; } // end namespace Catch @@ -1440,144 +2875,142 @@ namespace Catch { // end catch_decomposer.h // start catch_interfaces_capture.h +#include #include namespace Catch { + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + struct MessageBuilder; + struct Counts; + struct AssertionReaction; + struct SourceLineInfo; - class AssertionResult; - struct AssertionInfo; - struct SectionInfo; - struct SectionEndInfo; - struct MessageInfo; - struct Counts; - struct BenchmarkInfo; - struct BenchmarkStats; - struct AssertionReaction; + struct ITransientExpression; + struct IGeneratorTracker; - struct ITransientExpression; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo; + template> + struct BenchmarkStats; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - struct IResultCapture { + struct IResultCapture { + virtual ~IResultCapture(); - virtual ~IResultCapture(); + virtual bool sectionStarted(SectionInfo const §ionInfo, Counts &assertions) = 0; + virtual void sectionEnded(SectionEndInfo const &endInfo) = 0; + virtual void sectionEndedEarly(SectionEndInfo const &endInfo) = 0; - virtual bool sectionStarted( SectionInfo const& sectionInfo, - Counts& assertions ) = 0; - virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; - virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + virtual auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & = 0; - virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0; - virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing(std::string const &name) = 0; + virtual void benchmarkStarting(BenchmarkInfo const &info) = 0; + virtual void benchmarkEnded(BenchmarkStats<> const &stats) = 0; + virtual void benchmarkFailed(std::string const &error) = 0; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - virtual void pushScopedMessage( MessageInfo const& message ) = 0; - virtual void popScopedMessage( MessageInfo const& message ) = 0; + virtual void pushScopedMessage(MessageInfo const &message) = 0; + virtual void popScopedMessage(MessageInfo const &message) = 0; - virtual void handleFatalErrorCondition( StringRef message ) = 0; + virtual void emplaceUnscopedMessage(MessageBuilder const &builder) = 0; - virtual void handleExpr - ( AssertionInfo const& info, - ITransientExpression const& expr, - AssertionReaction& reaction ) = 0; - virtual void handleMessage - ( AssertionInfo const& info, - ResultWas::OfType resultType, - StringRef const& message, - AssertionReaction& reaction ) = 0; - virtual void handleUnexpectedExceptionNotThrown - ( AssertionInfo const& info, - AssertionReaction& reaction ) = 0; - virtual void handleUnexpectedInflightException - ( AssertionInfo const& info, - std::string const& message, - AssertionReaction& reaction ) = 0; - virtual void handleIncomplete - ( AssertionInfo const& info ) = 0; - virtual void handleNonExpr - ( AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction ) = 0; + virtual void handleFatalErrorCondition(StringRef message) = 0; - virtual bool lastAssertionPassed() = 0; - virtual void assertionPassed() = 0; + virtual void handleExpr(AssertionInfo const &info, ITransientExpression const &expr, AssertionReaction &reaction) = 0; + virtual void handleMessage(AssertionInfo const &info, ResultWas::OfType resultType, StringRef const &message, AssertionReaction &reaction) + = 0; + virtual void handleUnexpectedExceptionNotThrown(AssertionInfo const &info, AssertionReaction &reaction) = 0; + virtual void handleUnexpectedInflightException(AssertionInfo const &info, std::string const &message, AssertionReaction &reaction) = 0; + virtual void handleIncomplete(AssertionInfo const &info) = 0; + virtual void handleNonExpr(AssertionInfo const &info, ResultWas::OfType resultType, AssertionReaction &reaction) = 0; - // Deprecated, do not use: - virtual std::string getCurrentTestName() const = 0; - virtual const AssertionResult* getLastResult() const = 0; - virtual void exceptionEarlyReported() = 0; - }; + virtual bool lastAssertionPassed() = 0; + virtual void assertionPassed() = 0; - IResultCapture& getResultCapture(); -} + // Deprecated, do not use: + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult *getLastResult() const = 0; + virtual void exceptionEarlyReported() = 0; + }; + + IResultCapture &getResultCapture(); +} // namespace Catch // end catch_interfaces_capture.h namespace Catch { + struct TestFailureException { + }; + struct AssertionResultData; + struct IResultCapture; + class RunContext; - struct TestFailureException{}; - struct AssertionResultData; - struct IResultCapture; - class RunContext; + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; - class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; + ITransientExpression const *m_transientExpression = nullptr; + bool m_isNegated; - ITransientExpression const* m_transientExpression = nullptr; - bool m_isNegated; - public: - LazyExpression( bool isNegated ); - LazyExpression( LazyExpression const& other ); - LazyExpression& operator = ( LazyExpression const& ) = delete; + public: + LazyExpression(bool isNegated); + LazyExpression(LazyExpression const &other); + LazyExpression &operator=(LazyExpression const &) = delete; - explicit operator bool() const; + explicit operator bool() const; - friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; - }; + friend auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream &; + }; - struct AssertionReaction { - bool shouldDebugBreak = false; - bool shouldThrow = false; - }; + struct AssertionReaction { + bool shouldDebugBreak = false; + bool shouldThrow = false; + }; - class AssertionHandler { - AssertionInfo m_assertionInfo; - AssertionReaction m_reaction; - bool m_completed = false; - IResultCapture& m_resultCapture; + class AssertionHandler { + AssertionInfo m_assertionInfo; + AssertionReaction m_reaction; + bool m_completed = false; + IResultCapture &m_resultCapture; - public: - AssertionHandler - ( StringRef macroName, - SourceLineInfo const& lineInfo, - StringRef capturedExpression, - ResultDisposition::Flags resultDisposition ); - ~AssertionHandler() { - if ( !m_completed ) { - m_resultCapture.handleIncomplete( m_assertionInfo ); - } - } + public: + AssertionHandler(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition); + ~AssertionHandler() { + if (!m_completed) { + m_resultCapture.handleIncomplete(m_assertionInfo); + } + } - template - void handleExpr( ExprLhs const& expr ) { - handleExpr( expr.makeUnaryExpr() ); - } - void handleExpr( ITransientExpression const& expr ); + template + void handleExpr(ExprLhs const &expr) { + handleExpr(expr.makeUnaryExpr()); + } + void handleExpr(ITransientExpression const &expr); - void handleMessage(ResultWas::OfType resultType, StringRef const& message); + void handleMessage(ResultWas::OfType resultType, StringRef const &message); - void handleExceptionThrownAsExpected(); - void handleUnexpectedExceptionNotThrown(); - void handleExceptionNotThrownAsExpected(); - void handleThrowingCallSkipped(); - void handleUnexpectedInflightException(); + void handleExceptionThrownAsExpected(); + void handleUnexpectedExceptionNotThrown(); + void handleExceptionNotThrownAsExpected(); + void handleThrowingCallSkipped(); + void handleUnexpectedInflightException(); - void complete(); - void setCompleted(); + void complete(); + void setCompleted(); - // query - auto allowThrows() const -> bool; - }; + // query + auto allowThrows() const -> bool; + }; - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ); + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString); } // namespace Catch @@ -1585,58 +3018,80 @@ namespace Catch { // start catch_message.h #include +#include namespace Catch { + struct MessageInfo { + MessageInfo(StringRef const &_macroName, SourceLineInfo const &_lineInfo, ResultWas::OfType _type); - struct MessageInfo { - MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; - std::string macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - unsigned int sequence; + bool operator==(MessageInfo const &other) const; + bool operator<(MessageInfo const &other) const; - bool operator == ( MessageInfo const& other ) const; - bool operator < ( MessageInfo const& other ) const; - private: - static unsigned int globalCount; - }; + private: + static unsigned int globalCount; + }; - struct MessageStream { + struct MessageStream { + template + MessageStream &operator<<(T const &value) { + m_stream << value; + return *this; + } - template - MessageStream& operator << ( T const& value ) { - m_stream << value; - return *this; - } + ReusableStringStream m_stream; + }; - ReusableStringStream m_stream; - }; + struct MessageBuilder : MessageStream { + MessageBuilder(StringRef const ¯oName, SourceLineInfo const &lineInfo, ResultWas::OfType type); - struct MessageBuilder : MessageStream { - MessageBuilder( std::string const& macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType type ); + template + MessageBuilder &operator<<(T const &value) { + m_stream << value; + return *this; + } - template - MessageBuilder& operator << ( T const& value ) { - m_stream << value; - return *this; - } + MessageInfo m_info; + }; - MessageInfo m_info; - }; + class ScopedMessage { + public: + explicit ScopedMessage(MessageBuilder const &builder); + ScopedMessage(ScopedMessage &duplicate) = delete; + ScopedMessage(ScopedMessage &&old); + ~ScopedMessage(); - class ScopedMessage { - public: - explicit ScopedMessage( MessageBuilder const& builder ); - ~ScopedMessage(); + MessageInfo m_info; + bool m_moved; + }; - MessageInfo m_info; - }; + class Capturer { + std::vector m_messages; + IResultCapture &m_resultCapture = getResultCapture(); + size_t m_captured = 0; + + public: + Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names); + ~Capturer(); + + void captureValue(size_t index, std::string const &value); + + template + void captureValues(size_t index, T const &value) { + captureValue(index, Catch::Detail::stringify(value)); + } + + template + void captureValues(size_t index, T const &value, Ts const &...values) { + captureValue(index, Catch::Detail::stringify(value)); + captureValues(index + 1, values...); + } + }; } // end namespace Catch @@ -1644,131 +3099,158 @@ namespace Catch { #if !defined(CATCH_CONFIG_DISABLE) #if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) - #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ +#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ #else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" +#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" #endif -#if defined(CATCH_CONFIG_FAST_COMPILE) +#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// // Another way to speed-up compilation is to omit local try-catch for REQUIRE* // macros. #define INTERNAL_CATCH_TRY -#define INTERNAL_CATCH_CATCH( capturer ) +#define INTERNAL_CATCH_CATCH(capturer) #else // CATCH_CONFIG_FAST_COMPILE #define INTERNAL_CATCH_TRY try -#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } +#define INTERNAL_CATCH_CATCH(handler) \ + catch (...) { \ + handler.handleUnexpectedInflightException(); \ + } #endif -#define INTERNAL_CATCH_REACT( handler ) handler.complete(); +#define INTERNAL_CATCH_REACT(handler) handler.complete(); /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ - INTERNAL_CATCH_TRY { \ - CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \ - CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \ - } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( (void)0, false && static_cast( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look - // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. +#define INTERNAL_CATCH_TEST(macroName, resultDisposition, ...) \ + do { \ + CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + INTERNAL_CATCH_TRY { \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + catchAssertionHandler.handleExpr(Catch::Decomposer() <= __VA_ARGS__); \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + } \ + INTERNAL_CATCH_CATCH(catchAssertionHandler) \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while ((void)0, (false) && static_cast(!!(__VA_ARGS__))) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( Catch::getResultCapture().lastAssertionPassed() ) +#define INTERNAL_CATCH_IF(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (Catch::getResultCapture().lastAssertionPassed()) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \ - INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \ - if( !Catch::getResultCapture().lastAssertionPassed() ) +#define INTERNAL_CATCH_ELSE(macroName, resultDisposition, ...) \ + INTERNAL_CATCH_TEST(macroName, resultDisposition, __VA_ARGS__); \ + if (!Catch::getResultCapture().lastAssertionPassed()) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \ - try { \ - static_cast(__VA_ARGS__); \ - catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleUnexpectedInflightException(); \ - } \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_NO_THROW(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleExceptionNotThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \ - if( catchAssertionHandler.allowThrows() ) \ - try { \ - static_cast(__VA_ARGS__); \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleExceptionThrownAsExpected(); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_THROWS(macroName, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \ - if( catchAssertionHandler.allowThrows() ) \ - try { \ - static_cast(expr); \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( exceptionType const& ) { \ - catchAssertionHandler.handleExceptionThrownAsExpected(); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleUnexpectedInflightException(); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_THROWS_AS(macroName, exceptionType, resultDisposition, expr) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(expr); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (exceptionType const &) { \ + catchAssertionHandler.handleExceptionThrownAsExpected(); \ + } catch (...) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ - catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_MSG(macroName, messageType, resultDisposition, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition); \ + catchAssertionHandler.handleMessage(messageType, \ + (Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop()).m_stream.str()); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_INFO( macroName, log ) \ - Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ); +#define INTERNAL_CATCH_CAPTURE(varName, macroName, ...) \ + auto varName = Catch::Capturer(macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__); \ + varName.captureValues(0, __VA_ARGS__) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO(macroName, log) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME(scopedMessage)( \ + Catch::MessageBuilder(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_UNSCOPED_INFO(macroName, log) \ + Catch::getResultCapture().emplaceUnscopedMessage( \ + Catch::MessageBuilder(macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info) << log) /////////////////////////////////////////////////////////////////////////////// // Although this is matcher-based, it can be used with just a string -#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - if( catchAssertionHandler.allowThrows() ) \ - try { \ - static_cast(__VA_ARGS__); \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( ... ) { \ - Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher ); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_THROWS_STR_MATCHES(macroName, resultDisposition, matcher, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (...) { \ + Catch::handleExceptionMatchExpr(catchAssertionHandler, matcher, #matcher##_catch_sr); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) #endif // CATCH_CONFIG_DISABLE @@ -1782,56 +3264,53 @@ namespace Catch { #include namespace Catch { + struct Counts { + Counts operator-(Counts const &other) const; + Counts &operator+=(Counts const &other); - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); + std::size_t total() const; + bool allPassed() const; + bool allOk() const; - std::size_t total() const; - bool allPassed() const; - bool allOk() const; + std::size_t passed = 0; + std::size_t failed = 0; + std::size_t failedButOk = 0; + }; - std::size_t passed = 0; - std::size_t failed = 0; - std::size_t failedButOk = 0; - }; + struct Totals { + Totals operator-(Totals const &other) const; + Totals &operator+=(Totals const &other); - struct Totals { + Totals delta(Totals const &prevTotals) const; - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); - - Totals delta( Totals const& prevTotals ) const; - - int error = 0; - Counts assertions; - Counts testCases; - }; -} + int error = 0; + Counts assertions; + Counts testCases; + }; +} // namespace Catch // end catch_totals.h #include namespace Catch { + struct SectionInfo { + SectionInfo(SourceLineInfo const &_lineInfo, std::string const &_name); - struct SectionInfo { - SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description = std::string() ); + // Deprecated + SectionInfo(SourceLineInfo const &_lineInfo, std::string const &_name, std::string const &) + : SectionInfo(_lineInfo, _name) { + } - std::string name; - std::string description; - SourceLineInfo lineInfo; - }; + std::string name; + std::string description; // !Deprecated: this will always be empty + SourceLineInfo lineInfo; + }; - struct SectionEndInfo { - SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ); - - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; } // end namespace Catch @@ -1841,19 +3320,19 @@ namespace Catch { #include namespace Catch { + auto getCurrentNanosecondsSinceEpoch() -> uint64_t; + auto getEstimatedClockResolution() -> uint64_t; - auto getCurrentNanosecondsSinceEpoch() -> uint64_t; - auto getEstimatedClockResolution() -> uint64_t; + class Timer { + uint64_t m_nanoseconds = 0; - class Timer { - uint64_t m_nanoseconds = 0; - public: - void start(); - auto getElapsedNanoseconds() const -> uint64_t; - auto getElapsedMicroseconds() const -> uint64_t; - auto getElapsedMilliseconds() const -> unsigned int; - auto getElapsedSeconds() const -> double; - }; + public: + void start(); + auto getElapsedNanoseconds() const -> uint64_t; + auto getElapsedMicroseconds() const -> uint64_t; + auto getElapsedMilliseconds() const -> unsigned int; + auto getElapsedSeconds() const -> double; + }; } // namespace Catch @@ -1861,129 +3340,93 @@ namespace Catch { #include namespace Catch { + class Section : NonCopyable { + public: + Section(SectionInfo const &info); + ~Section(); - class Section : NonCopyable { - public: - Section( SectionInfo const& info ); - ~Section(); + // This indicates whether the section should be executed or not + explicit operator bool() const; - // This indicates whether the section should be executed or not - explicit operator bool() const; + private: + SectionInfo m_info; - private: - SectionInfo m_info; - - std::string m_name; - Counts m_assertions; - bool m_sectionIncluded; - Timer m_timer; - }; + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; } // end namespace Catch - #define INTERNAL_CATCH_SECTION( ... ) \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#define INTERNAL_CATCH_SECTION(...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if (Catch::Section const &INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) \ + = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, __VA_ARGS__)) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#define INTERNAL_CATCH_DYNAMIC_SECTION(...) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + if (Catch::Section const &INTERNAL_CATCH_UNIQUE_NAME(catch_internal_Section) \ + = Catch::SectionInfo(CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str())) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION // end catch_section.h -// start catch_benchmark.h - -#include -#include - -namespace Catch { - - class BenchmarkLooper { - - std::string m_name; - std::size_t m_count = 0; - std::size_t m_iterationsToRun = 1; - uint64_t m_resolution; - Timer m_timer; - - static auto getResolution() -> uint64_t; - public: - // Keep most of this inline as it's on the code path that is being timed - BenchmarkLooper( StringRef name ) - : m_name( name ), - m_resolution( getResolution() ) - { - reportStart(); - m_timer.start(); - } - - explicit operator bool() { - if( m_count < m_iterationsToRun ) - return true; - return needsMoreIterations(); - } - - void increment() { - ++m_count; - } - - void reportStart(); - auto needsMoreIterations() -> bool; - }; - -} // end namespace Catch - -#define BENCHMARK( name ) \ - for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() ) - -// end catch_benchmark.h // start catch_interfaces_exception.h // start catch_interfaces_registry_hub.h -#include #include +#include namespace Catch { + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + struct ITagAliasRegistry; + struct IMutableEnumValuesRegistry; - class TestCase; - struct ITestCaseRegistry; - struct IExceptionTranslatorRegistry; - struct IExceptionTranslator; - struct IReporterRegistry; - struct IReporterFactory; - struct ITagAliasRegistry; - class StartupExceptionRegistry; + class StartupExceptionRegistry; - using IReporterFactoryPtr = std::shared_ptr; + using IReporterFactoryPtr = std::shared_ptr; - struct IRegistryHub { - virtual ~IRegistryHub(); + struct IRegistryHub { + virtual ~IRegistryHub(); - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IReporterRegistry const &getReporterRegistry() const = 0; + virtual ITestCaseRegistry const &getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const &getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const = 0; - virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + virtual StartupExceptionRegistry const &getStartupExceptionRegistry() const = 0; + }; - virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; - }; + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) = 0; + virtual void registerListener(IReporterFactoryPtr const &factory) = 0; + virtual void registerTest(TestCase const &testInfo) = 0; + virtual void registerTranslator(const IExceptionTranslator *translator) = 0; + virtual void registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() = 0; + }; - struct IMutableRegistryHub { - virtual ~IMutableRegistryHub(); - virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0; - virtual void registerListener( IReporterFactoryPtr const& factory ) = 0; - virtual void registerTest( TestCase const& testInfo ) = 0; - virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; - virtual void registerStartupException() noexcept = 0; - }; + IRegistryHub const &getRegistryHub(); + IMutableRegistryHub &getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); - IRegistryHub& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} +} // namespace Catch // end catch_interfaces_registry_hub.h #if defined(CATCH_CONFIG_DISABLE) - #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \ - static std::string translatorName( signature ) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG(translatorName, signature) static std::string translatorName(signature) #endif #include @@ -1991,215 +3434,225 @@ namespace Catch { #include namespace Catch { - using exceptionTranslateFunction = std::string(*)(); + using exceptionTranslateFunction = std::string (*)(); - struct IExceptionTranslator; - using ExceptionTranslators = std::vector>; + struct IExceptionTranslator; + using ExceptionTranslators = std::vector>; - struct IExceptionTranslator { - virtual ~IExceptionTranslator(); - virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; - }; + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const = 0; + }; - struct IExceptionTranslatorRegistry { - virtual ~IExceptionTranslatorRegistry(); + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); - virtual std::string translateActiveException() const = 0; - }; - - class ExceptionTranslatorRegistrar { - template - class ExceptionTranslator : public IExceptionTranslator { - public: - - ExceptionTranslator( std::string(*translateFunction)( T& ) ) - : m_translateFunction( translateFunction ) - {} - - std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override { - try { - if( it == itEnd ) - std::rethrow_exception(std::current_exception()); - else - return (*it)->translate( it+1, itEnd ); - } - catch( T& ex ) { - return m_translateFunction( ex ); - } - } - - protected: - std::string(*m_translateFunction)( T& ); - }; + virtual std::string translateActiveException() const = 0; + }; + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { public: - template - ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { - getMutableRegistryHub().registerTranslator - ( new ExceptionTranslator( translateFunction ) ); + ExceptionTranslator(std::string (*translateFunction)(T &)) + : m_translateFunction(translateFunction) { + } + + std::string translate(ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd) const override { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return ""; +#else + try { + if (it == itEnd) + std::rethrow_exception(std::current_exception()); + else + return (*it)->translate(it + 1, itEnd); + } catch (T &ex) { + return m_translateFunction(ex); } +#endif + } + + protected: + std::string (*m_translateFunction)(T &); }; -} + + public: + template + ExceptionTranslatorRegistrar(std::string (*translateFunction)(T &)) { + getMutableRegistryHub().registerTranslator(new ExceptionTranslator(translateFunction)); + } + }; +} // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ - static std::string translatorName( signature ); \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \ - static std::string translatorName( signature ) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2(translatorName, signature) \ + static std::string translatorName(signature); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionRegistrar)(&translatorName); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ + static std::string translatorName(signature) -#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) \ + INTERNAL_CATCH_TRANSLATE_EXCEPTION2(INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator), signature) // end catch_interfaces_exception.h // start catch_approx.h #include -#include namespace Catch { -namespace Detail { - + namespace Detail { class Approx { private: - bool equalityComparisonImpl(double other) const; + bool equalityComparisonImpl(double other) const; + // Validates the new margin (margin >= 0) + // out-of-line to avoid including stdexcept in the header + void setMargin(double margin); + // Validates the new epsilon (0 < epsilon < 1) + // out-of-line to avoid including stdexcept in the header + void setEpsilon(double epsilon); public: - explicit Approx ( double value ); + explicit Approx(double value); - static Approx custom(); + static Approx custom(); - template ::value>::type> - Approx operator()( T const& value ) { - Approx approx( static_cast(value) ); - approx.epsilon( m_epsilon ); - approx.margin( m_margin ); - approx.scale( m_scale ); - return approx; - } + Approx operator-() const; - template ::value>::type> - explicit Approx( T const& value ): Approx(static_cast(value)) - {} + template::value>::type> + Approx operator()(T const &value) { + Approx approx(static_cast(value)); + approx.m_epsilon = m_epsilon; + approx.m_margin = m_margin; + approx.m_scale = m_scale; + return approx; + } - template ::value>::type> - friend bool operator == ( const T& lhs, Approx const& rhs ) { - auto lhs_v = static_cast(lhs); - return rhs.equalityComparisonImpl(lhs_v); - } + template::value>::type> + explicit Approx(T const &value) + : Approx(static_cast(value)) { + } - template ::value>::type> - friend bool operator == ( Approx const& lhs, const T& rhs ) { - return operator==( rhs, lhs ); - } + template::value>::type> + friend bool operator==(const T &lhs, Approx const &rhs) { + auto lhs_v = static_cast(lhs); + return rhs.equalityComparisonImpl(lhs_v); + } - template ::value>::type> - friend bool operator != ( T const& lhs, Approx const& rhs ) { - return !operator==( lhs, rhs ); - } + template::value>::type> + friend bool operator==(Approx const &lhs, const T &rhs) { + return operator==(rhs, lhs); + } - template ::value>::type> - friend bool operator != ( Approx const& lhs, T const& rhs ) { - return !operator==( rhs, lhs ); - } + template::value>::type> + friend bool operator!=(T const &lhs, Approx const &rhs) { + return !operator==(lhs, rhs); + } - template ::value>::type> - friend bool operator <= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) < rhs.m_value || lhs == rhs; - } + template::value>::type> + friend bool operator!=(Approx const &lhs, T const &rhs) { + return !operator==(rhs, lhs); + } - template ::value>::type> - friend bool operator <= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value < static_cast(rhs) || lhs == rhs; - } + template::value>::type> + friend bool operator<=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } - template ::value>::type> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; - } + template::value>::type> + friend bool operator<=(Approx const &lhs, T const &rhs) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } - template ::value>::type> - friend bool operator >= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } + template::value>::type> + friend bool operator>=(T const &lhs, Approx const &rhs) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } - template ::value>::type> - Approx& epsilon( T const& newEpsilon ) { - double epsilonAsDouble = static_cast(newEpsilon); - if( epsilonAsDouble < 0 || epsilonAsDouble > 1.0 ) { - throw std::domain_error - ( "Invalid Approx::epsilon: " + - Catch::Detail::stringify( epsilonAsDouble ) + - ", Approx::epsilon has to be between 0 and 1" ); - } - m_epsilon = epsilonAsDouble; - return *this; - } + template::value>::type> + friend bool operator>=(Approx const &lhs, T const &rhs) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } - template ::value>::type> - Approx& margin( T const& newMargin ) { - double marginAsDouble = static_cast(newMargin); - if( marginAsDouble < 0 ) { - throw std::domain_error - ( "Invalid Approx::margin: " + - Catch::Detail::stringify( marginAsDouble ) + - ", Approx::Margin has to be non-negative." ); + template::value>::type> + Approx &epsilon(T const &newEpsilon) { + double epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } - } - m_margin = marginAsDouble; - return *this; - } + template::value>::type> + Approx &margin(T const &newMargin) { + double marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } - template ::value>::type> - Approx& scale( T const& newScale ) { - m_scale = static_cast(newScale); - return *this; - } + template::value>::type> + Approx &scale(T const &newScale) { + m_scale = static_cast(newScale); + return *this; + } - std::string toString() const; + std::string toString() const; private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; + double m_epsilon; + double m_margin; + double m_scale; + double m_value; }; -} + } // end namespace Detail -template<> -struct StringMaker { - static std::string convert(Catch::Detail::Approx const& value); -}; + namespace literals { + Detail::Approx operator"" _a(long double val); + Detail::Approx operator"" _a(unsigned long long val); + } // end namespace literals + + template<> + struct StringMaker { + static std::string convert(Catch::Detail::Approx const &value); + }; } // end namespace Catch // end catch_approx.h // start catch_string_manip.h -#include #include +#include +#include namespace Catch { + bool startsWith(std::string const &s, std::string const &prefix); + bool startsWith(std::string const &s, char prefix); + bool endsWith(std::string const &s, std::string const &suffix); + bool endsWith(std::string const &s, char suffix); + bool contains(std::string const &s, std::string const &infix); + void toLowerInPlace(std::string &s); + std::string toLower(std::string const &s); + //! Returns a new string without whitespace at the start/end + std::string trim(std::string const &str); + //! Returns a substring of the original ref without whitespace. Beware lifetimes! + StringRef trim(StringRef ref); - bool startsWith( std::string const& s, std::string const& prefix ); - bool startsWith( std::string const& s, char prefix ); - bool endsWith( std::string const& s, std::string const& suffix ); - bool endsWith( std::string const& s, char suffix ); - bool contains( std::string const& s, std::string const& infix ); - void toLowerInPlace( std::string& s ); - std::string toLower( std::string const& s ); - std::string trim( std::string const& str ); - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + // !!! Be aware, returns refs into original string - make sure original string outlives them + std::vector splitStringRef(StringRef str, char delimiter); + bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis); - struct pluralise { - pluralise( std::size_t count, std::string const& label ); + struct pluralise { + pluralise(std::size_t count, std::string const &label); - friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + friend std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser); - std::size_t m_count; - std::string m_label; - }; -} + std::size_t m_count; + std::string m_label; + }; +} // namespace Catch // end catch_string_manip.h #ifndef CATCH_CONFIG_DISABLE_MATCHERS @@ -2211,187 +3664,244 @@ namespace Catch { #include namespace Catch { -namespace Matchers { + namespace Matchers { namespace Impl { + template + struct MatchAllOf; + template + struct MatchAnyOf; + template + struct MatchNotOf; - template struct MatchAllOf; - template struct MatchAnyOf; - template struct MatchNotOf; + class MatcherUntypedBase { + public: + MatcherUntypedBase() = default; + MatcherUntypedBase(MatcherUntypedBase const &) = default; + MatcherUntypedBase &operator=(MatcherUntypedBase const &) = delete; + std::string toString() const; - class MatcherUntypedBase { - public: - MatcherUntypedBase() = default; - MatcherUntypedBase ( MatcherUntypedBase const& ) = default; - MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete; - std::string toString() const; + protected: + virtual ~MatcherUntypedBase(); + virtual std::string describe() const = 0; + mutable std::string m_cachedToString; + }; - protected: - virtual ~MatcherUntypedBase(); - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; - }; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-virtual-dtor" +#endif - template - struct MatcherMethod { - virtual bool match( ObjectT const& arg ) const = 0; - }; - template - struct MatcherMethod { - virtual bool match( PtrT* arg ) const = 0; - }; + template + struct MatcherMethod { + virtual bool match(ObjectT const &arg) const = 0; + }; - template - struct MatcherBase : MatcherUntypedBase, MatcherMethod { +#if defined(__OBJC__) + // Hack to fix Catch GH issue #1661. Could use id for generic Object support. + // use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation + template<> + struct MatcherMethod { + virtual bool match(NSString *arg) const = 0; + }; +#endif - MatchAllOf operator && ( MatcherBase const& other ) const; - MatchAnyOf operator || ( MatcherBase const& other ) const; - MatchNotOf operator ! () const; - }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif - template - struct MatchAllOf : MatcherBase { - bool match( ArgT const& arg ) const override { - for( auto matcher : m_matchers ) { - if (!matcher->match(arg)) - return false; - } - return true; - } - std::string describe() const override { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - bool first = true; - for( auto matcher : m_matchers ) { - if( first ) - first = false; - else - description += " and "; - description += matcher->toString(); - } - description += " )"; - return description; - } + template + struct MatcherBase : MatcherUntypedBase, MatcherMethod { + MatchAllOf operator&&(MatcherBase const &other) const; + MatchAnyOf operator||(MatcherBase const &other) const; + MatchNotOf operator!() const; + }; - MatchAllOf& operator && ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - template - struct MatchAnyOf : MatcherBase { - - bool match( ArgT const& arg ) const override { - for( auto matcher : m_matchers ) { - if (matcher->match(arg)) - return true; - } - return false; - } - std::string describe() const override { - std::string description; - description.reserve( 4 + m_matchers.size()*32 ); - description += "( "; - bool first = true; - for( auto matcher : m_matchers ) { - if( first ) - first = false; - else - description += " or "; - description += matcher->toString(); - } - description += " )"; - return description; - } - - MatchAnyOf& operator || ( MatcherBase const& other ) { - m_matchers.push_back( &other ); - return *this; - } - - std::vector const*> m_matchers; - }; - - template - struct MatchNotOf : MatcherBase { - - MatchNotOf( MatcherBase const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {} - - bool match( ArgT const& arg ) const override { - return !m_underlyingMatcher.match( arg ); - } - - std::string describe() const override { - return "not " + m_underlyingMatcher.toString(); - } - MatcherBase const& m_underlyingMatcher; - }; - - template - MatchAllOf MatcherBase::operator && ( MatcherBase const& other ) const { - return MatchAllOf() && *this && other; + template + struct MatchAllOf : MatcherBase { + bool match(ArgT const &arg) const override { + for (auto matcher : m_matchers) { + if (!matcher->match(arg)) + return false; + } + return true; } - template - MatchAnyOf MatcherBase::operator || ( MatcherBase const& other ) const { - return MatchAnyOf() || *this || other; + std::string describe() const override { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher : m_matchers) { + if (first) + first = false; + else + description += " and "; + description += matcher->toString(); + } + description += " )"; + return description; } - template - MatchNotOf MatcherBase::operator ! () const { - return MatchNotOf( *this ); + + MatchAllOf operator&&(MatcherBase const &other) { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; } + std::vector const *> m_matchers; + }; + template + struct MatchAnyOf : MatcherBase { + bool match(ArgT const &arg) const override { + for (auto matcher : m_matchers) { + if (matcher->match(arg)) + return true; + } + return false; + } + std::string describe() const override { + std::string description; + description.reserve(4 + m_matchers.size() * 32); + description += "( "; + bool first = true; + for (auto matcher : m_matchers) { + if (first) + first = false; + else + description += " or "; + description += matcher->toString(); + } + description += " )"; + return description; + } + + MatchAnyOf operator||(MatcherBase const &other) { + auto copy(*this); + copy.m_matchers.push_back(&other); + return copy; + } + + std::vector const *> m_matchers; + }; + + template + struct MatchNotOf : MatcherBase { + MatchNotOf(MatcherBase const &underlyingMatcher) + : m_underlyingMatcher(underlyingMatcher) { + } + + bool match(ArgT const &arg) const override { return !m_underlyingMatcher.match(arg); } + + std::string describe() const override { return "not " + m_underlyingMatcher.toString(); } + MatcherBase const &m_underlyingMatcher; + }; + + template + MatchAllOf MatcherBase::operator&&(MatcherBase const &other) const { + return MatchAllOf() && *this && other; + } + template + MatchAnyOf MatcherBase::operator||(MatcherBase const &other) const { + return MatchAnyOf() || *this || other; + } + template + MatchNotOf MatcherBase::operator!() const { + return MatchNotOf(*this); + } + } // namespace Impl + } // namespace Matchers -} // namespace Matchers - -using namespace Matchers; -using Matchers::Impl::MatcherBase; + using namespace Matchers; + using Matchers::Impl::MatcherBase; } // namespace Catch // end catch_matchers.h -// start catch_matchers_floating.h - -#include -#include +// start catch_matchers_exception.hpp namespace Catch { -namespace Matchers { + namespace Matchers { + namespace Exception { + class ExceptionMessageMatcher : public MatcherBase { + std::string m_message; + public: + ExceptionMessageMatcher(std::string const &message) + : m_message(message) { + } + + bool match(std::exception const &ex) const override; + + std::string describe() const override; + }; + + } // namespace Exception + + Exception::ExceptionMessageMatcher Message(std::string const &message); + + } // namespace Matchers +} // namespace Catch + +// end catch_matchers_exception.hpp +// start catch_matchers_floating.h + +namespace Catch { + namespace Matchers { namespace Floating { + enum class FloatingPointKind : uint8_t; - enum class FloatingPointKind : uint8_t; + struct WithinAbsMatcher : MatcherBase { + WithinAbsMatcher(double target, double margin); + bool match(double const &matchee) const override; + std::string describe() const override; - struct WithinAbsMatcher : MatcherBase { - WithinAbsMatcher(double target, double margin); - bool match(double const& matchee) const override; - std::string describe() const override; - private: - double m_target; - double m_margin; - }; + private: + double m_target; + double m_margin; + }; - struct WithinUlpsMatcher : MatcherBase { - WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType); - bool match(double const& matchee) const override; - std::string describe() const override; - private: - double m_target; - int m_ulps; - FloatingPointKind m_type; - }; + struct WithinUlpsMatcher : MatcherBase { + WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType); + bool match(double const &matchee) const override; + std::string describe() const override; + + private: + double m_target; + uint64_t m_ulps; + FloatingPointKind m_type; + }; + + // Given IEEE-754 format for floats and doubles, we can assume + // that float -> double promotion is lossless. Given this, we can + // assume that if we do the standard relative comparison of + // |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get + // the same result if we do this for floats, as if we do this for + // doubles that were promoted from floats. + struct WithinRelMatcher : MatcherBase { + WithinRelMatcher(double target, double epsilon); + bool match(double const &matchee) const override; + std::string describe() const override; + + private: + double m_target; + double m_epsilon; + }; } // namespace Floating // The following functions create the actual matcher objects. // This allows the types to be inferred - Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff); - Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); + Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); Floating::WithinAbsMatcher WithinAbs(double target, double margin); + Floating::WithinRelMatcher WithinRel(double target, double eps); + // defaults epsilon to 100*numeric_limits::epsilon() + Floating::WithinRelMatcher WithinRel(double target); + Floating::WithinRelMatcher WithinRel(float target, float eps); + // defaults epsilon to 100*numeric_limits::epsilon() + Floating::WithinRelMatcher WithinRel(float target); -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_floating.h @@ -2401,45 +3911,40 @@ namespace Matchers { #include namespace Catch { -namespace Matchers { -namespace Generic { + namespace Matchers { + namespace Generic { + namespace Detail { + std::string finalizeDescription(const std::string &desc); + } -namespace Detail { - std::string finalizeDescription(const std::string& desc); -} + template + class PredicateMatcher : public MatcherBase { + std::function m_predicate; + std::string m_description; -template -class PredicateMatcher : public MatcherBase { - std::function m_predicate; - std::string m_description; -public: + public: + PredicateMatcher(std::function const &elem, std::string const &descr) + : m_predicate(std::move(elem)) + , m_description(Detail::finalizeDescription(descr)) { + } - PredicateMatcher(std::function const& elem, std::string const& descr) - :m_predicate(std::move(elem)), - m_description(Detail::finalizeDescription(descr)) - {} + bool match(T const &item) const override { return m_predicate(item); } - bool match( T const& item ) const override { - return m_predicate(item); - } + std::string describe() const override { return m_description; } + }; - std::string describe() const override { - return m_description; - } -}; - -} // namespace Generic + } // namespace Generic // The following functions create the actual matcher objects. // The user has to explicitly specify type to the function, because - // infering std::function is hard (but possible) and + // inferring std::function is hard (but possible) and // requires a lot of TMP. template - Generic::PredicateMatcher Predicate(std::function const& predicate, std::string const& description = "") { - return Generic::PredicateMatcher(predicate, description); + Generic::PredicateMatcher Predicate(std::function const &predicate, std::string const &description = "") { + return Generic::PredicateMatcher(predicate, description); } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_generic.hpp @@ -2448,67 +3953,64 @@ public: #include namespace Catch { -namespace Matchers { - + namespace Matchers { namespace StdString { + struct CasedString { + CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity); + std::string adjustString(std::string const &str) const; + std::string caseSensitivitySuffix() const; - struct CasedString - { - CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ); - std::string adjustString( std::string const& str ) const; - std::string caseSensitivitySuffix() const; + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; - CaseSensitive::Choice m_caseSensitivity; - std::string m_str; - }; + struct StringMatcherBase : MatcherBase { + StringMatcherBase(std::string const &operation, CasedString const &comparator); + std::string describe() const override; - struct StringMatcherBase : MatcherBase { - StringMatcherBase( std::string const& operation, CasedString const& comparator ); - std::string describe() const override; + CasedString m_comparator; + std::string m_operation; + }; - CasedString m_comparator; - std::string m_operation; - }; + struct EqualsMatcher : StringMatcherBase { + EqualsMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; + }; + struct ContainsMatcher : StringMatcherBase { + ContainsMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; + }; + struct StartsWithMatcher : StringMatcherBase { + StartsWithMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; + }; + struct EndsWithMatcher : StringMatcherBase { + EndsWithMatcher(CasedString const &comparator); + bool match(std::string const &source) const override; + }; - struct EqualsMatcher : StringMatcherBase { - EqualsMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; - }; - struct ContainsMatcher : StringMatcherBase { - ContainsMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; - }; - struct StartsWithMatcher : StringMatcherBase { - StartsWithMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; - }; - struct EndsWithMatcher : StringMatcherBase { - EndsWithMatcher( CasedString const& comparator ); - bool match( std::string const& source ) const override; - }; + struct RegexMatcher : MatcherBase { + RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity); + bool match(std::string const &matchee) const override; + std::string describe() const override; - struct RegexMatcher : MatcherBase { - RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity ); - bool match( std::string const& matchee ) const override; - std::string describe() const override; - - private: - std::string m_regex; - CaseSensitive::Choice m_caseSensitivity; - }; + private: + std::string m_regex; + CaseSensitive::Choice m_caseSensitivity; + }; } // namespace StdString // The following functions create the actual matcher objects. // This allows the types to be inferred - StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); - StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ); + StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + StdString::ContainsMatcher Contains(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + StdString::EndsWithMatcher EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + StdString::StartsWithMatcher StartsWith(std::string const &str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); + StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes); -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_string.h @@ -2517,250 +4019,1113 @@ namespace Matchers { #include namespace Catch { -namespace Matchers { - + namespace Matchers { namespace Vector { - namespace Detail { - template - size_t count(InputIterator first, InputIterator last, T const& item) { - size_t cnt = 0; - for (; first != last; ++first) { - if (*first == item) { - ++cnt; - } - } - return cnt; - } - template - bool contains(InputIterator first, InputIterator last, T const& item) { - for (; first != last; ++first) { - if (*first == item) { - return true; - } - } - return false; - } + template + struct ContainsElementMatcher : MatcherBase> { + ContainsElementMatcher(T const &comparator) + : m_comparator(comparator) { } - template - struct ContainsElementMatcher : MatcherBase> { - - ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {} - - bool match(std::vector const &v) const override { - for (auto const& el : v) { - if (el == m_comparator) { - return true; - } - } - return false; + bool match(std::vector const &v) const override { + for (auto const &el : v) { + if (el == m_comparator) { + return true; } + } + return false; + } - std::string describe() const override { - return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + std::string describe() const override { return "Contains: " + ::Catch::Detail::stringify(m_comparator); } + + T const &m_comparator; + }; + + template + struct ContainsMatcher : MatcherBase> { + ContainsMatcher(std::vector const &comparator) + : m_comparator(comparator) { + } + + bool match(std::vector const &v) const override { + // !TBD: see note in EqualsMatcher + if (m_comparator.size() > v.size()) + return false; + for (auto const &comparator : m_comparator) { + auto present = false; + for (const auto &el : v) { + if (el == comparator) { + present = true; + break; + } } - - T const& m_comparator; - }; - - template - struct ContainsMatcher : MatcherBase> { - - ContainsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} - - bool match(std::vector const &v) const override { - // !TBD: see note in EqualsMatcher - if (m_comparator.size() > v.size()) - return false; - for (auto const& comparator : m_comparator) { - auto present = false; - for (const auto& el : v) { - if (el == comparator) { - present = true; - break; - } - } - if (!present) { - return false; - } - } - return true; - } - std::string describe() const override { - return "Contains: " + ::Catch::Detail::stringify( m_comparator ); + if (!present) { + return false; } + } + return true; + } + std::string describe() const override { return "Contains: " + ::Catch::Detail::stringify(m_comparator); } - std::vector const& m_comparator; - }; + std::vector const &m_comparator; + }; - template - struct EqualsMatcher : MatcherBase> { + template + struct EqualsMatcher : MatcherBase> { + EqualsMatcher(std::vector const &comparator) + : m_comparator(comparator) { + } - EqualsMatcher(std::vector const &comparator) : m_comparator( comparator ) {} + bool match(std::vector const &v) const override { + // !TBD: This currently works if all elements can be compared using != + // - a more general approach would be via a compare template that defaults + // to using !=. but could be specialised for, e.g. std::vector etc + // - then just call that directly + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != v[i]) + return false; + return true; + } + std::string describe() const override { return "Equals: " + ::Catch::Detail::stringify(m_comparator); } + std::vector const &m_comparator; + }; - bool match(std::vector const &v) const override { - // !TBD: This currently works if all elements can be compared using != - // - a more general approach would be via a compare template that defaults - // to using !=. but could be specialised for, e.g. std::vector etc - // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; - return true; - } - std::string describe() const override { - return "Equals: " + ::Catch::Detail::stringify( m_comparator ); - } - std::vector const& m_comparator; - }; + template + struct ApproxMatcher : MatcherBase> { + ApproxMatcher(std::vector const &comparator) + : m_comparator(comparator) { + } - template - struct UnorderedEqualsMatcher : MatcherBase> { - UnorderedEqualsMatcher(std::vector const& target) : m_target(target) {} - bool match(std::vector const& vec) const override { - // Note: This is a reimplementation of std::is_permutation, - // because I don't want to include inside the common path - if (m_target.size() != vec.size()) { - return false; - } - auto lfirst = m_target.begin(), llast = m_target.end(); - auto rfirst = vec.begin(), rlast = vec.end(); - // Cut common prefix to optimize checking of permuted parts - while (lfirst != llast && *lfirst != *rfirst) { - ++lfirst; ++rfirst; - } - if (lfirst == llast) { - return true; - } + bool match(std::vector const &v) const override { + if (m_comparator.size() != v.size()) + return false; + for (std::size_t i = 0; i < v.size(); ++i) + if (m_comparator[i] != approx(v[i])) + return false; + return true; + } + std::string describe() const override { return "is approx: " + ::Catch::Detail::stringify(m_comparator); } + template::value>::type> + ApproxMatcher &epsilon(T const &newEpsilon) { + approx.epsilon(newEpsilon); + return *this; + } + template::value>::type> + ApproxMatcher &margin(T const &newMargin) { + approx.margin(newMargin); + return *this; + } + template::value>::type> + ApproxMatcher &scale(T const &newScale) { + approx.scale(newScale); + return *this; + } - for (auto mid = lfirst; mid != llast; ++mid) { - // Skip already counted items - if (Detail::contains(lfirst, mid, *mid)) { - continue; - } - size_t num_vec = Detail::count(rfirst, rlast, *mid); - if (num_vec == 0 || Detail::count(lfirst, llast, *mid) != num_vec) { - return false; - } - } + std::vector const &m_comparator; + mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom(); + }; - return true; - } + template + struct UnorderedEqualsMatcher : MatcherBase> { + UnorderedEqualsMatcher(std::vector const &target) + : m_target(target) { + } + bool match(std::vector const &vec) const override { + if (m_target.size() != vec.size()) { + return false; + } + return std::is_permutation(m_target.begin(), m_target.end(), vec.begin()); + } - std::string describe() const override { - return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); - } - private: - std::vector const& m_target; - }; + std::string describe() const override { return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target); } + + private: + std::vector const &m_target; + }; } // namespace Vector // The following functions create the actual matcher objects. // This allows the types to be inferred - template - Vector::ContainsMatcher Contains( std::vector const& comparator ) { - return Vector::ContainsMatcher( comparator ); + template, typename AllocMatch = AllocComp> + Vector::ContainsMatcher Contains(std::vector const &comparator) { + return Vector::ContainsMatcher(comparator); } - template - Vector::ContainsElementMatcher VectorContains( T const& comparator ) { - return Vector::ContainsElementMatcher( comparator ); + template> + Vector::ContainsElementMatcher VectorContains(T const &comparator) { + return Vector::ContainsElementMatcher(comparator); } - template - Vector::EqualsMatcher Equals( std::vector const& comparator ) { - return Vector::EqualsMatcher( comparator ); + template, typename AllocMatch = AllocComp> + Vector::EqualsMatcher Equals(std::vector const &comparator) { + return Vector::EqualsMatcher(comparator); } - template - Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const& target) { - return Vector::UnorderedEqualsMatcher(target); + template, typename AllocMatch = AllocComp> + Vector::ApproxMatcher Approx(std::vector const &comparator) { + return Vector::ApproxMatcher(comparator); } -} // namespace Matchers + template, typename AllocMatch = AllocComp> + Vector::UnorderedEqualsMatcher UnorderedEquals(std::vector const &target) { + return Vector::UnorderedEqualsMatcher(target); + } + + } // namespace Matchers } // namespace Catch // end catch_matchers_vector.h namespace Catch { + template + class MatchExpr : public ITransientExpression { + ArgT const &m_arg; + MatcherT m_matcher; + StringRef m_matcherString; - template - class MatchExpr : public ITransientExpression { - ArgT const& m_arg; - MatcherT m_matcher; - StringRef m_matcherString; - public: - MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) - : ITransientExpression{ true, matcher.match( arg ) }, - m_arg( arg ), - m_matcher( matcher ), - m_matcherString( matcherString ) - {} - - void streamReconstructedExpression( std::ostream &os ) const override { - auto matcherAsString = m_matcher.toString(); - os << Catch::Detail::stringify( m_arg ) << ' '; - if( matcherAsString == Detail::unprintableString ) - os << m_matcherString; - else - os << matcherAsString; - } - }; - - using StringMatcher = Matchers::Impl::MatcherBase; - - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ); - - template - auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef matcherString ) -> MatchExpr { - return MatchExpr( arg, matcher, matcherString ); + public: + MatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) + : ITransientExpression{true, matcher.match(arg)} + , m_arg(arg) + , m_matcher(matcher) + , m_matcherString(matcherString) { } + void streamReconstructedExpression(std::ostream &os) const override { + auto matcherAsString = m_matcher.toString(); + os << Catch::Detail::stringify(m_arg) << ' '; + if (matcherAsString == Detail::unprintableString) + os << m_matcherString; + else + os << matcherAsString; + } + }; + + using StringMatcher = Matchers::Impl::MatcherBase; + + void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString); + + template + auto makeMatchExpr(ArgT const &arg, MatcherT const &matcher, StringRef const &matcherString) -> MatchExpr { + return MatchExpr(arg, matcher, matcherString); + } + } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - INTERNAL_CATCH_TRY { \ - catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher ) ); \ - } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CHECK_THAT(macroName, matcher, resultDisposition, arg) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + INTERNAL_CATCH_TRY { catchAssertionHandler.handleExpr(Catch::makeMatchExpr(arg, matcher, #matcher##_catch_sr)); } \ + INTERNAL_CATCH_CATCH(catchAssertionHandler) \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) /////////////////////////////////////////////////////////////////////////////// -#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \ - do { \ - Catch::AssertionHandler catchAssertionHandler( macroName, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \ - if( catchAssertionHandler.allowThrows() ) \ - try { \ - static_cast(__VA_ARGS__ ); \ - catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ - } \ - catch( exceptionType const& ex ) { \ - catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher ) ); \ - } \ - catch( ... ) { \ - catchAssertionHandler.handleUnexpectedInflightException(); \ - } \ - else \ - catchAssertionHandler.handleThrowingCallSkipped(); \ - INTERNAL_CATCH_REACT( catchAssertionHandler ) \ - } while( false ) +#define INTERNAL_CATCH_THROWS_MATCHES(macroName, exceptionType, resultDisposition, matcher, ...) \ + do { \ + Catch::AssertionHandler catchAssertionHandler(macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY( \ + exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), \ + resultDisposition); \ + if (catchAssertionHandler.allowThrows()) \ + try { \ + static_cast(__VA_ARGS__); \ + catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \ + } catch (exceptionType const &ex) { \ + catchAssertionHandler.handleExpr(Catch::makeMatchExpr(ex, matcher, #matcher##_catch_sr)); \ + } catch (...) { \ + catchAssertionHandler.handleUnexpectedInflightException(); \ + } \ + else \ + catchAssertionHandler.handleThrowingCallSkipped(); \ + INTERNAL_CATCH_REACT(catchAssertionHandler) \ + } while (false) // end catch_capture_matchers.h #endif +// start catch_generators.hpp + +// start catch_interfaces_generatortracker.h + +#include + +namespace Catch { + namespace Generators { + class GeneratorUntypedBase { + public: + GeneratorUntypedBase() = default; + virtual ~GeneratorUntypedBase(); + // Attempts to move the generator to the next element + // + // Returns true iff the move succeeded (and a valid element + // can be retrieved). + virtual bool next() = 0; + }; + using GeneratorBasePtr = std::unique_ptr; + + } // namespace Generators + + struct IGeneratorTracker { + virtual ~IGeneratorTracker(); + virtual auto hasGenerator() const -> bool = 0; + virtual auto getGenerator() const -> Generators::GeneratorBasePtr const & = 0; + virtual void setGenerator(Generators::GeneratorBasePtr &&generator) = 0; + }; + +} // namespace Catch + +// end catch_interfaces_generatortracker.h +// start catch_enforce.h + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + template + [[noreturn]] void throw_exception(Ex const &e) { + throw e; + } +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + [[noreturn]] void throw_exception(std::exception const &e); +#endif + + [[noreturn]] void throw_logic_error(std::string const &msg); + [[noreturn]] void throw_domain_error(std::string const &msg); + [[noreturn]] void throw_runtime_error(std::string const &msg); + +} // namespace Catch + +#define CATCH_MAKE_MSG(...) (Catch::ReusableStringStream() << __VA_ARGS__).str() + +#define CATCH_INTERNAL_ERROR(...) \ + Catch::throw_logic_error(CATCH_MAKE_MSG(CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__)) + +#define CATCH_ERROR(...) Catch::throw_domain_error(CATCH_MAKE_MSG(__VA_ARGS__)) + +#define CATCH_RUNTIME_ERROR(...) Catch::throw_runtime_error(CATCH_MAKE_MSG(__VA_ARGS__)) + +#define CATCH_ENFORCE(condition, ...) \ + do { \ + if (!(condition)) \ + CATCH_ERROR(__VA_ARGS__); \ + } while (false) + +// end catch_enforce.h +#include +#include +#include + +#include +#include + +namespace Catch { + class GeneratorException : public std::exception { + const char *const m_msg = ""; + + public: + GeneratorException(const char *msg) + : m_msg(msg) { + } + + const char *what() const noexcept override final; + }; + + namespace Generators { + // !TBD move this into its own location? + namespace pf { + template + std::unique_ptr make_unique(Args &&...args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + } // namespace pf + + template + struct IGenerator : GeneratorUntypedBase { + virtual ~IGenerator() = default; + + // Returns the current element of the generator + // + // \Precondition The generator is either freshly constructed, + // or the last call to `next()` returned true + virtual T const &get() const = 0; + using type = T; + }; + + template + class SingleValueGenerator final : public IGenerator { + T m_value; + + public: + SingleValueGenerator(T &&value) + : m_value(std::move(value)) { + } + + T const &get() const override { return m_value; } + bool next() override { return false; } + }; + + template + class FixedValuesGenerator final : public IGenerator { + static_assert(!std::is_same::value, + "FixedValuesGenerator does not support bools because of std::vector" + "specialization, use SingleValue Generator instead."); + std::vector m_values; + size_t m_idx = 0; + + public: + FixedValuesGenerator(std::initializer_list values) + : m_values(values) { + } + + T const &get() const override { return m_values[m_idx]; } + bool next() override { + ++m_idx; + return m_idx < m_values.size(); + } + }; + + template + class GeneratorWrapper final { + std::unique_ptr> m_generator; + + public: + GeneratorWrapper(std::unique_ptr> generator) + : m_generator(std::move(generator)) { + } + T const &get() const { return m_generator->get(); } + bool next() { return m_generator->next(); } + }; + + template + GeneratorWrapper value(T &&value) { + return GeneratorWrapper(pf::make_unique>(std::forward(value))); + } + template + GeneratorWrapper values(std::initializer_list values) { + return GeneratorWrapper(pf::make_unique>(values)); + } + + template + class Generators : public IGenerator { + std::vector> m_generators; + size_t m_current = 0; + + void populate(GeneratorWrapper &&generator) { m_generators.emplace_back(std::move(generator)); } + void populate(T &&val) { m_generators.emplace_back(value(std::forward(val))); } + template + void populate(U &&val) { + populate(T(std::forward(val))); + } + template + void populate(U &&valueOrGenerator, Gs &&...moreGenerators) { + populate(std::forward(valueOrGenerator)); + populate(std::forward(moreGenerators)...); + } + + public: + template + Generators(Gs &&...moreGenerators) { + m_generators.reserve(sizeof...(Gs)); + populate(std::forward(moreGenerators)...); + } + + T const &get() const override { return m_generators[m_current].get(); } + + bool next() override { + if (m_current >= m_generators.size()) { + return false; + } + const bool current_status = m_generators[m_current].next(); + if (!current_status) { + ++m_current; + } + return m_current < m_generators.size(); + } + }; + + template + GeneratorWrapper> table(std::initializer_list::type...>> tuples) { + return values>(tuples); + } + + // Tag type to signal that a generator sequence should convert arguments to a specific type + template + struct as { + }; + + template + auto makeGenerators(GeneratorWrapper &&generator, Gs &&...moreGenerators) -> Generators { + return Generators(std::move(generator), std::forward(moreGenerators)...); + } + template + auto makeGenerators(GeneratorWrapper &&generator) -> Generators { + return Generators(std::move(generator)); + } + template + auto makeGenerators(T &&val, Gs &&...moreGenerators) -> Generators { + return makeGenerators(value(std::forward(val)), std::forward(moreGenerators)...); + } + template + auto makeGenerators(as, U &&val, Gs &&...moreGenerators) -> Generators { + return makeGenerators(value(T(std::forward(val))), std::forward(moreGenerators)...); + } + + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker &; + + template + // Note: The type after -> is weird, because VS2015 cannot parse + // the expression used in the typedef inside, when it is in + // return type. Yeah. + auto generate(StringRef generatorName, SourceLineInfo const &lineInfo, L const &generatorExpression) + -> decltype(std::declval().get()) { + using UnderlyingType = typename decltype(generatorExpression())::type; + + IGeneratorTracker &tracker = acquireGeneratorTracker(generatorName, lineInfo); + if (!tracker.hasGenerator()) { + tracker.setGenerator(pf::make_unique>(generatorExpression())); + } + + auto const &generator = static_cast const &>(*tracker.getGenerator()); + return generator.get(); + } + + } // namespace Generators +} // namespace Catch + +#define GENERATE(...) \ + Catch::Generators::generate(INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), CATCH_INTERNAL_LINEINFO, [] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) +#define GENERATE_COPY(...) \ + Catch::Generators::generate(INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), CATCH_INTERNAL_LINEINFO, [=] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) +#define GENERATE_REF(...) \ + Catch::Generators::generate(INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), CATCH_INTERNAL_LINEINFO, [&] { \ + using namespace Catch::Generators; \ + return makeGenerators(__VA_ARGS__); \ + }) // NOLINT(google-build-using-namespace) + +// end catch_generators.hpp +// start catch_generators_generic.hpp + +namespace Catch { + namespace Generators { + template + class TakeGenerator : public IGenerator { + GeneratorWrapper m_generator; + size_t m_returned = 0; + size_t m_target; + + public: + TakeGenerator(size_t target, GeneratorWrapper &&generator) + : m_generator(std::move(generator)) + , m_target(target) { + assert(target != 0 && "Empty generators are not allowed"); + } + T const &get() const override { return m_generator.get(); } + bool next() override { + ++m_returned; + if (m_returned >= m_target) { + return false; + } + + const auto success = m_generator.next(); + // If the underlying generator does not contain enough values + // then we cut short as well + if (!success) { + m_returned = m_target; + } + return success; + } + }; + + template + GeneratorWrapper take(size_t target, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(target, std::move(generator))); + } + + template + class FilterGenerator : public IGenerator { + GeneratorWrapper m_generator; + Predicate m_predicate; + + public: + template + FilterGenerator(P &&pred, GeneratorWrapper &&generator) + : m_generator(std::move(generator)) + , m_predicate(std::forward

(pred)) { + if (!m_predicate(m_generator.get())) { + // It might happen that there are no values that pass the + // filter. In that case we throw an exception. + auto has_initial_value = next(); + if (!has_initial_value) { + Catch::throw_exception(GeneratorException("No valid value found in filtered generator")); + } + } + } + + T const &get() const override { return m_generator.get(); } + + bool next() override { + bool success = m_generator.next(); + if (!success) { + return false; + } + while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true) + ; + return success; + } + }; + + template + GeneratorWrapper filter(Predicate &&pred, GeneratorWrapper &&generator) { + return GeneratorWrapper( + std::unique_ptr>(pf::make_unique>(std::forward(pred), std::move(generator)))); + } + + template + class RepeatGenerator : public IGenerator { + static_assert(!std::is_same::value, + "RepeatGenerator currently does not support bools" + "because of std::vector specialization"); + GeneratorWrapper m_generator; + mutable std::vector m_returned; + size_t m_target_repeats; + size_t m_current_repeat = 0; + size_t m_repeat_index = 0; + + public: + RepeatGenerator(size_t repeats, GeneratorWrapper &&generator) + : m_generator(std::move(generator)) + , m_target_repeats(repeats) { + assert(m_target_repeats > 0 && "Repeat generator must repeat at least once"); + } + + T const &get() const override { + if (m_current_repeat == 0) { + m_returned.push_back(m_generator.get()); + return m_returned.back(); + } + return m_returned[m_repeat_index]; + } + + bool next() override { + // There are 2 basic cases: + // 1) We are still reading the generator + // 2) We are reading our own cache + + // In the first case, we need to poke the underlying generator. + // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache + if (m_current_repeat == 0) { + const auto success = m_generator.next(); + if (!success) { + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + + // In the second case, we need to move indices forward and check that we haven't run up against the end + ++m_repeat_index; + if (m_repeat_index == m_returned.size()) { + m_repeat_index = 0; + ++m_current_repeat; + } + return m_current_repeat < m_target_repeats; + } + }; + + template + GeneratorWrapper repeat(size_t repeats, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(repeats, std::move(generator))); + } + + template + class MapGenerator : public IGenerator { + // TBD: provide static assert for mapping function, for friendly error message + GeneratorWrapper m_generator; + Func m_function; + // To avoid returning dangling reference, we have to save the values + T m_cache; + + public: + template + MapGenerator(F2 &&function, GeneratorWrapper &&generator) + : m_generator(std::move(generator)) + , m_function(std::forward(function)) + , m_cache(m_function(m_generator.get())) { + } + + T const &get() const override { return m_cache; } + bool next() override { + const auto success = m_generator.next(); + if (success) { + m_cache = m_function(m_generator.get()); + } + return success; + } + }; + + template> + GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(std::forward(function), std::move(generator))); + } + + template + GeneratorWrapper map(Func &&function, GeneratorWrapper &&generator) { + return GeneratorWrapper(pf::make_unique>(std::forward(function), std::move(generator))); + } + + template + class ChunkGenerator final : public IGenerator> { + std::vector m_chunk; + size_t m_chunk_size; + GeneratorWrapper m_generator; + bool m_used_up = false; + + public: + ChunkGenerator(size_t size, GeneratorWrapper generator) + : m_chunk_size(size) + , m_generator(std::move(generator)) { + m_chunk.reserve(m_chunk_size); + if (m_chunk_size != 0) { + m_chunk.push_back(m_generator.get()); + for (size_t i = 1; i < m_chunk_size; ++i) { + if (!m_generator.next()) { + Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk")); + } + m_chunk.push_back(m_generator.get()); + } + } + } + std::vector const &get() const override { return m_chunk; } + bool next() override { + m_chunk.clear(); + for (size_t idx = 0; idx < m_chunk_size; ++idx) { + if (!m_generator.next()) { + return false; + } + m_chunk.push_back(m_generator.get()); + } + return true; + } + }; + + template + GeneratorWrapper> chunk(size_t size, GeneratorWrapper &&generator) { + return GeneratorWrapper>(pf::make_unique>(size, std::move(generator))); + } + + } // namespace Generators +} // namespace Catch + +// end catch_generators_generic.hpp +// start catch_generators_specific.hpp + +// start catch_context.h + +#include + +namespace Catch { + struct IResultCapture; + struct IRunner; + struct IConfig; + struct IMutableContext; + + using IConfigPtr = std::shared_ptr; + + struct IContext { + virtual ~IContext(); + + virtual IResultCapture *getResultCapture() = 0; + virtual IRunner *getRunner() = 0; + virtual IConfigPtr const &getConfig() const = 0; + }; + + struct IMutableContext : IContext { + virtual ~IMutableContext(); + virtual void setResultCapture(IResultCapture *resultCapture) = 0; + virtual void setRunner(IRunner *runner) = 0; + virtual void setConfig(IConfigPtr const &config) = 0; + + private: + static IMutableContext *currentContext; + friend IMutableContext &getCurrentMutableContext(); + friend void cleanUpContext(); + static void createContext(); + }; + + inline IMutableContext &getCurrentMutableContext() { + if (!IMutableContext::currentContext) + IMutableContext::createContext(); + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *IMutableContext::currentContext; + } + + inline IContext &getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext(); + + class SimplePcg32; + SimplePcg32 &rng(); +} // namespace Catch + +// end catch_context.h +// start catch_interfaces_config.h + +// start catch_option.hpp + +namespace Catch { + // An optional type + template + class Option { + public: + Option() + : nullableValue(nullptr) { + } + Option(T const &_value) + : nullableValue(new (storage) T(_value)) { + } + Option(Option const &_other) + : nullableValue(_other ? new (storage) T(*_other) : nullptr) { + } + + ~Option() { reset(); } + + Option &operator=(Option const &_other) { + if (&_other != this) { + reset(); + if (_other) + nullableValue = new (storage) T(*_other); + } + return *this; + } + Option &operator=(T const &_value) { + reset(); + nullableValue = new (storage) T(_value); + return *this; + } + + void reset() { + if (nullableValue) + nullableValue->~T(); + nullableValue = nullptr; + } + + T &operator*() { return *nullableValue; } + T const &operator*() const { return *nullableValue; } + T *operator->() { return nullableValue; } + const T *operator->() const { return nullableValue; } + + T valueOr(T const &defaultValue) const { return nullableValue ? *nullableValue : defaultValue; } + + bool some() const { return nullableValue != nullptr; } + bool none() const { return nullableValue == nullptr; } + + bool operator!() const { return nullableValue == nullptr; } + explicit operator bool() const { return some(); } + + private: + T *nullableValue; + alignas(alignof(T)) char storage[sizeof(T)]; + }; + +} // end namespace Catch + +// end catch_option.hpp +#include +#include +#include +#include +#include + +namespace Catch { + enum class Verbosity { + Quiet = 0, + Normal, + High + }; + + struct WarnAbout { + enum What { + Nothing = 0x00, + NoAssertions = 0x01, + NoTests = 0x02 + }; + }; + + struct ShowDurations { + enum OrNot { + DefaultForReporter, + Always, + Never + }; + }; + struct RunTests { + enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; + }; + struct UseColour { + enum YesOrNo { + Auto, + Yes, + No + }; + }; + struct WaitForKeypress { + enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; + }; + + class TestSpec; + + struct IConfig : NonCopyable { + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream &stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutNoTests() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const &testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const &getTestsOrTags() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const &getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool benchmarkNoAnalysis() const = 0; + virtual int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; + }; + + using IConfigPtr = std::shared_ptr; +} // namespace Catch + +// end catch_interfaces_config.h +// start catch_random_number_generator.h + +#include + +namespace Catch { + // This is a simple implementation of C++11 Uniform Random Number + // Generator. It does not provide all operators, because Catch2 + // does not use it, but it should behave as expected inside stdlib's + // distributions. + // The implementation is based on the PCG family (http://pcg-random.org) + class SimplePcg32 { + using state_type = std::uint64_t; + + public: + using result_type = std::uint32_t; + static constexpr result_type(min)() { return 0; } + static constexpr result_type(max)() { return static_cast(-1); } + + // Provide some default initial state for the default constructor + SimplePcg32() + : SimplePcg32(0xed743cc4U) { + } + + explicit SimplePcg32(result_type seed_); + + void seed(result_type seed_); + void discard(uint64_t skip); + + result_type operator()(); + + private: + friend bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + friend bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs); + + // In theory we also need operator<< and operator>> + // In practice we do not use them, so we will skip them for now + + std::uint64_t m_state; + // This part of the state determines which "stream" of the numbers + // is chosen -- we take it as a constant for Catch2, so we only + // need to deal with seeding the main state. + // Picked by reading 8 bytes from `/dev/random` :-) + static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL; + }; + +} // end namespace Catch + +// end catch_random_number_generator.h +#include + +namespace Catch { + namespace Generators { + template + class RandomFloatingGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_real_distribution m_dist; + Float m_current_number; + + public: + RandomFloatingGenerator(Float a, Float b) + : m_rng(rng()) + , m_dist(a, b) { + static_cast(next()); + } + + Float const &get() const override { return m_current_number; } + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } + }; + + template + class RandomIntegerGenerator final : public IGenerator { + Catch::SimplePcg32 &m_rng; + std::uniform_int_distribution m_dist; + Integer m_current_number; + + public: + RandomIntegerGenerator(Integer a, Integer b) + : m_rng(rng()) + , m_dist(a, b) { + static_cast(next()); + } + + Integer const &get() const override { return m_current_number; } + bool next() override { + m_current_number = m_dist(m_rng); + return true; + } + }; + + // TODO: Ideally this would be also constrained against the various char types, + // but I don't expect users to run into that in practice. + template + typename std::enable_if::value && !std::is_same::value, GeneratorWrapper>::type random(T a, T b) { + return GeneratorWrapper(pf::make_unique>(a, b)); + } + + template + typename std::enable_if::value, GeneratorWrapper>::type random(T a, T b) { + return GeneratorWrapper(pf::make_unique>(a, b)); + } + + template + class RangeGenerator final : public IGenerator { + T m_current; + T m_end; + T m_step; + bool m_positive; + + public: + RangeGenerator(T const &start, T const &end, T const &step) + : m_current(start) + , m_end(end) + , m_step(step) + , m_positive(m_step > T(0)) { + assert(m_current != m_end && "Range start and end cannot be equal"); + assert(m_step != T(0) && "Step size cannot be zero"); + assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end"); + } + + RangeGenerator(T const &start, T const &end) + : RangeGenerator(start, end, (start < end) ? T(1) : T(-1)) { + } + + T const &get() const override { return m_current; } + + bool next() override { + m_current += m_step; + return (m_positive) ? (m_current < m_end) : (m_current > m_end); + } + }; + + template + GeneratorWrapper range(T const &start, T const &end, T const &step) { + static_assert(std::is_arithmetic::value && !std::is_same::value, "Type must be numeric"); + return GeneratorWrapper(pf::make_unique>(start, end, step)); + } + + template + GeneratorWrapper range(T const &start, T const &end) { + static_assert(std::is_integral::value && !std::is_same::value, "Type must be an integer"); + return GeneratorWrapper(pf::make_unique>(start, end)); + } + + template + class IteratorGenerator final : public IGenerator { + static_assert(!std::is_same::value, + "IteratorGenerator currently does not support bools" + "because of std::vector specialization"); + + std::vector m_elems; + size_t m_current = 0; + + public: + template + IteratorGenerator(InputIterator first, InputSentinel last) + : m_elems(first, last) { + if (m_elems.empty()) { + Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values")); + } + } + + T const &get() const override { return m_elems[m_current]; } + + bool next() override { + ++m_current; + return m_current != m_elems.size(); + } + }; + + template::value_type> + GeneratorWrapper from_range(InputIterator from, InputSentinel to) { + return GeneratorWrapper(pf::make_unique>(from, to)); + } + + template + GeneratorWrapper from_range(Container const &cnt) { + return GeneratorWrapper(pf::make_unique>(cnt.begin(), cnt.end())); + } + + } // namespace Generators +} // namespace Catch + +// end catch_generators_specific.hpp // These files are included here so the single_include script doesn't put them // in the conditionally compiled sections // start catch_test_case_info.h +#include #include #include -#include #ifdef __clang__ #pragma clang diagnostic push @@ -2768,67 +5133,62 @@ namespace Catch { #endif namespace Catch { + struct ITestInvoker; - struct ITestInvoker; - - struct TestCaseInfo { - enum SpecialProperties{ - None = 0, - IsHidden = 1 << 1, - ShouldFail = 1 << 2, - MayFail = 1 << 3, - Throws = 1 << 4, - NonPortable = 1 << 5, - Benchmark = 1 << 6 - }; - - TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::vector const& _tags, - SourceLineInfo const& _lineInfo ); - - friend void setTags( TestCaseInfo& testCaseInfo, std::vector tags ); - - bool isHidden() const; - bool throws() const; - bool okToFail() const; - bool expectedToFail() const; - - std::string tagsAsString() const; - - std::string name; - std::string className; - std::string description; - std::vector tags; - std::vector lcaseTags; - SourceLineInfo lineInfo; - SpecialProperties properties; + struct TestCaseInfo { + enum SpecialProperties { + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5, + Benchmark = 1 << 6 }; - class TestCase : public TestCaseInfo { - public: + TestCaseInfo(std::string const &_name, + std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo); - TestCase( ITestInvoker* testCase, TestCaseInfo&& info ); + friend void setTags(TestCaseInfo &testCaseInfo, std::vector tags); - TestCase withName( std::string const& _newName ) const; + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; - void invoke() const; + std::string tagsAsString() const; - TestCaseInfo const& getTestCaseInfo() const; + std::string name; + std::string className; + std::string description; + std::vector tags; + std::vector lcaseTags; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; - bool operator == ( TestCase const& other ) const; - bool operator < ( TestCase const& other ) const; + class TestCase : public TestCaseInfo { + public: + TestCase(ITestInvoker *testCase, TestCaseInfo &&info); - private: - std::shared_ptr test; - }; + TestCase withName(std::string const &_newName) const; - TestCase makeTestCase( ITestInvoker* testCase, - std::string const& className, - NameAndTags const& nameAndTags, - SourceLineInfo const& lineInfo ); -} + void invoke() const; + + TestCaseInfo const &getTestCaseInfo() const; + + bool operator==(TestCase const &other) const; + bool operator<(TestCase const &other) const; + + private: + std::shared_ptr test; + }; + + TestCase makeTestCase(ITestInvoker *testCase, std::string const &className, NameAndTags const &nameAndTags, SourceLineInfo const &lineInfo); +} // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop @@ -2838,12 +5198,11 @@ namespace Catch { // start catch_interfaces_runner.h namespace Catch { - - struct IRunner { - virtual ~IRunner(); - virtual bool aborting() const = 0; - }; -} + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} // namespace Catch // end catch_interfaces_runner.h @@ -2865,193 +5224,187 @@ namespace Catch { @optional --(void) setUp; --(void) tearDown; +- (void)setUp; +- (void)tearDown; @end namespace Catch { - - class OcMethod : public ITestInvoker { - - public: - OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} - - virtual void invoke() const { - id obj = [[m_cls alloc] init]; - - performOptionalSelector( obj, @selector(setUp) ); - performOptionalSelector( obj, m_sel ); - performOptionalSelector( obj, @selector(tearDown) ); - - arcSafeRelease( obj ); - } - private: - virtual ~OcMethod() {} - - Class m_cls; - SEL m_sel; - }; - - namespace Detail{ - - inline std::string getAnnotation( Class cls, - std::string const& annotationName, - std::string const& testCaseName ) { - NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; - SEL sel = NSSelectorFromString( selStr ); - arcSafeRelease( selStr ); - id value = performOptionalSelector( cls, sel ); - if( value ) - return [(NSString*)value UTF8String]; - return ""; - } + class OcMethod : public ITestInvoker { + public: + OcMethod(Class cls, SEL sel) + : m_cls(cls) + , m_sel(sel) { } - inline std::size_t registerTestMethods() { - std::size_t noTestMethods = 0; - int noClasses = objc_getClassList( nullptr, 0 ); + virtual void invoke() const { + id obj = [[m_cls alloc] init]; - Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); - objc_getClassList( classes, noClasses ); + performOptionalSelector(obj, @selector(setUp)); + performOptionalSelector(obj, m_sel); + performOptionalSelector(obj, @selector(tearDown)); - for( int c = 0; c < noClasses; c++ ) { - Class cls = classes[c]; - { - u_int count; - Method* methods = class_copyMethodList( cls, &count ); - for( u_int m = 0; m < count ; m++ ) { - SEL selector = method_getName(methods[m]); - std::string methodName = sel_getName(selector); - if( startsWith( methodName, "Catch_TestCase_" ) ) { - std::string testCaseName = methodName.substr( 15 ); - std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); - std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); - const char* className = class_getName( cls ); - - getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo("",0) ) ); - noTestMethods++; - } - } - free(methods); - } - } - return noTestMethods; + arcSafeRelease(obj); } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail { + inline std::string getAnnotation(Class cls, std::string const &annotationName, std::string const &testCaseName) { + NSString *selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString(selStr); + arcSafeRelease(selStr); + id value = performOptionalSelector(cls, sel); + if (value) + return [(NSString *)value UTF8String]; + return ""; + } + } // namespace Detail + + inline std::size_t registerTestMethods() { + std::size_t noTestMethods = 0; + int noClasses = objc_getClassList(nullptr, 0); + + Class *classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc(sizeof(Class) * noClasses); + objc_getClassList(classes, noClasses); + + for (int c = 0; c < noClasses; c++) { + Class cls = classes[c]; + { + u_int count; + Method *methods = class_copyMethodList(cls, &count); + for (u_int m = 0; m < count; m++) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if (startsWith(methodName, "Catch_TestCase_")) { + std::string testCaseName = methodName.substr(15); + std::string name = Detail::getAnnotation(cls, "Name", testCaseName); + std::string desc = Detail::getAnnotation(cls, "Description", testCaseName); + const char *className = class_getName(cls); + + getMutableRegistryHub().registerTest( + makeTestCase(new OcMethod(cls, selector), className, NameAndTags(name.c_str(), desc.c_str()), SourceLineInfo("", 0))); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) - namespace Matchers { - namespace Impl { - namespace NSStringMatchers { + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + struct StringHolder : MatcherBase { + StringHolder(NSString *substr) + : m_substr([substr copy]) { + } + StringHolder(StringHolder const &other) + : m_substr([other.m_substr copy]) { + } + StringHolder() { arcSafeRelease(m_substr); } - struct StringHolder : MatcherBase{ - StringHolder( NSString* substr ) : m_substr( [substr copy] ){} - StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} - StringHolder() { - arcSafeRelease( m_substr ); - } + bool match(NSString *str) const override { return false; } - bool match( NSString* arg ) const override { - return false; - } + NSString *CATCH_ARC_STRONG m_substr; + }; - NSString* CATCH_ARC_STRONG m_substr; - }; + struct Equals : StringHolder { + Equals(NSString *substr) + : StringHolder(substr) { + } - struct Equals : StringHolder { - Equals( NSString* substr ) : StringHolder( substr ){} + bool match(NSString *str) const override { return (str != nil || m_substr == nil) && [str isEqualToString:m_substr]; } - bool match( NSString* str ) const override { - return (str != nil || m_substr == nil ) && - [str isEqualToString:m_substr]; - } + std::string describe() const override { return "equals string: " + Catch::Detail::stringify(m_substr); } + }; - std::string describe() const override { - return "equals string: " + Catch::Detail::stringify( m_substr ); - } - }; + struct Contains : StringHolder { + Contains(NSString *substr) + : StringHolder(substr) { + } - struct Contains : StringHolder { - Contains( NSString* substr ) : StringHolder( substr ){} + bool match(NSString *str) const override { + return (str != nil || m_substr == nil) && [str rangeOfString:m_substr].location != NSNotFound; + } - bool match( NSString* str ) const { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location != NSNotFound; - } + std::string describe() const override { return "contains string: " + Catch::Detail::stringify(m_substr); } + }; - std::string describe() const override { - return "contains string: " + Catch::Detail::stringify( m_substr ); - } - }; + struct StartsWith : StringHolder { + StartsWith(NSString *substr) + : StringHolder(substr) { + } - struct StartsWith : StringHolder { - StartsWith( NSString* substr ) : StringHolder( substr ){} + bool match(NSString *str) const override { return (str != nil || m_substr == nil) && [str rangeOfString:m_substr].location == 0; } - bool match( NSString* str ) const override { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == 0; - } + std::string describe() const override { return "starts with: " + Catch::Detail::stringify(m_substr); } + }; + struct EndsWith : StringHolder { + EndsWith(NSString *substr) + : StringHolder(substr) { + } - std::string describe() const override { - return "starts with: " + Catch::Detail::stringify( m_substr ); - } - }; - struct EndsWith : StringHolder { - EndsWith( NSString* substr ) : StringHolder( substr ){} + bool match(NSString *str) const override { + return (str != nil || m_substr == nil) && [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } - bool match( NSString* str ) const override { - return (str != nil || m_substr == nil ) && - [str rangeOfString:m_substr].location == [str length] - [m_substr length]; - } + std::string describe() const override { return "ends with: " + Catch::Detail::stringify(m_substr); } + }; - std::string describe() const override { - return "ends with: " + Catch::Detail::stringify( m_substr ); - } - }; + } // namespace NSStringMatchers + } // namespace Impl - } // namespace NSStringMatchers - } // namespace Impl + inline Impl::NSStringMatchers::Equals Equals(NSString *substr) { + return Impl::NSStringMatchers::Equals(substr); + } - inline Impl::NSStringMatchers::Equals - Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + inline Impl::NSStringMatchers::Contains Contains(NSString *substr) { + return Impl::NSStringMatchers::Contains(substr); + } - inline Impl::NSStringMatchers::Contains - Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + inline Impl::NSStringMatchers::StartsWith StartsWith(NSString *substr) { + return Impl::NSStringMatchers::StartsWith(substr); + } - inline Impl::NSStringMatchers::StartsWith - StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + inline Impl::NSStringMatchers::EndsWith EndsWith(NSString *substr) { + return Impl::NSStringMatchers::EndsWith(substr); + } - inline Impl::NSStringMatchers::EndsWith - EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + } // namespace Matchers - } // namespace Matchers - - using namespace Matchers; + using namespace Matchers; #endif // CATCH_CONFIG_DISABLE_MATCHERS } // namespace Catch /////////////////////////////////////////////////////////////////////////////// -#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix -#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \ -+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \ -{ \ -return @ name; \ -} \ -+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \ -{ \ -return @ desc; \ -} \ --(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix ) +#define OC_MAKE_UNIQUE_NAME(root, uniqueSuffix) root##uniqueSuffix +#define OC_TEST_CASE2(name, desc, uniqueSuffix) \ + +(NSString *)OC_MAKE_UNIQUE_NAME(Catch_Name_test_, uniqueSuffix) { \ + return @name; \ + } \ + +(NSString *)OC_MAKE_UNIQUE_NAME(Catch_Description_test_, uniqueSuffix) { \ + return @desc; \ + } \ + -(void)OC_MAKE_UNIQUE_NAME(Catch_TestCase_test_, uniqueSuffix) -#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ ) +#define OC_TEST_CASE(name, desc) OC_TEST_CASE2(name, desc, __LINE__) // end catch_objc.hpp #endif -#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES +// Benchmarking needs the externally-facing parts of reporters to work +#if defined(CATCH_CONFIG_EXTERNAL_INTERFACES) || defined(CATCH_CONFIG_ENABLE_BENCHMARKING) // start catch_external_interfaces.h // start catch_reporter_bases.hpp @@ -3076,87 +5429,102 @@ return @ desc; \ // start catch_wildcard_pattern.h -namespace Catch -{ - class WildcardPattern { - enum WildcardPosition { - NoWildcard = 0, - WildcardAtStart = 1, - WildcardAtEnd = 2, - WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd - }; - - public: - - WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ); - virtual ~WildcardPattern() = default; - virtual bool matches( std::string const& str ) const; - - private: - std::string adjustCase( std::string const& str ) const; - CaseSensitive::Choice m_caseSensitivity; - WildcardPosition m_wildcard = NoWildcard; - std::string m_pattern; +namespace Catch { + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd }; -} + + public: + WildcardPattern(std::string const &pattern, CaseSensitive::Choice caseSensitivity); + virtual ~WildcardPattern() = default; + virtual bool matches(std::string const &str) const; + + private: + std::string normaliseString(std::string const &str) const; + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard = NoWildcard; + std::string m_pattern; + }; +} // namespace Catch // end catch_wildcard_pattern.h +#include #include #include -#include namespace Catch { + struct IConfig; - class TestSpec { - struct Pattern { - virtual ~Pattern(); - virtual bool matches( TestCaseInfo const& testCase ) const = 0; - }; - using PatternPtr = std::shared_ptr; - - class NamePattern : public Pattern { - public: - NamePattern( std::string const& name ); - virtual ~NamePattern(); - virtual bool matches( TestCaseInfo const& testCase ) const override; - private: - WildcardPattern m_wildcardPattern; - }; - - class TagPattern : public Pattern { - public: - TagPattern( std::string const& tag ); - virtual ~TagPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const override; - private: - std::string m_tag; - }; - - class ExcludedPattern : public Pattern { - public: - ExcludedPattern( PatternPtr const& underlyingPattern ); - virtual ~ExcludedPattern(); - virtual bool matches( TestCaseInfo const& testCase ) const override; - private: - PatternPtr m_underlyingPattern; - }; - - struct Filter { - std::vector m_patterns; - - bool matches( TestCaseInfo const& testCase ) const; - }; - + class TestSpec { + class Pattern { public: - bool hasFilters() const; - bool matches( TestCaseInfo const& testCase ) const; + explicit Pattern(std::string const &name); + virtual ~Pattern(); + virtual bool matches(TestCaseInfo const &testCase) const = 0; + std::string const &name() const; private: - std::vector m_filters; - - friend class TestSpecParser; + std::string const m_name; }; -} + using PatternPtr = std::shared_ptr; + + class NamePattern : public Pattern { + public: + explicit NamePattern(std::string const &name, std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + explicit TagPattern(std::string const &tag, std::string const &filterString); + bool matches(TestCaseInfo const &testCase) const override; + + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + explicit ExcludedPattern(PatternPtr const &underlyingPattern); + bool matches(TestCaseInfo const &testCase) const override; + + private: + PatternPtr m_underlyingPattern; + }; + + struct Filter { + std::vector m_patterns; + + bool matches(TestCaseInfo const &testCase) const; + std::string name() const; + }; + + public: + struct FilterMatch { + std::string name; + std::vector tests; + }; + using Matches = std::vector; + using vectorStrings = std::vector; + + bool hasFilters() const; + bool matches(TestCaseInfo const &testCase) const; + Matches matchesByFilter(std::vector const &testCases, IConfig const &config) const; + const vectorStrings &getInvalidArgs() const; + + private: + std::vector m_filters; + std::vector m_invalidArgs; + friend class TestSpecParser; + }; +} // namespace Catch #ifdef __clang__ #pragma clang diagnostic pop @@ -3168,69 +5536,76 @@ namespace Catch { #include namespace Catch { + struct TagAlias; - struct TagAlias; + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + // Nullptr if not present + virtual TagAlias const *find(std::string const &alias) const = 0; + virtual std::string expandAliases(std::string const &unexpandedTestSpec) const = 0; - struct ITagAliasRegistry { - virtual ~ITagAliasRegistry(); - // Nullptr if not present - virtual TagAlias const* find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - - static ITagAliasRegistry const& get(); - }; + static ITagAliasRegistry const &get(); + }; } // end namespace Catch // end catch_interfaces_tag_alias_registry.h namespace Catch { - - class TestSpecParser { - enum Mode{ None, Name, QuotedName, Tag, EscapedName }; - Mode m_mode = None; - bool m_exclusion = false; - std::size_t m_start = std::string::npos, m_pos = 0; - std::string m_arg; - std::vector m_escapeChars; - TestSpec::Filter m_currentFilter; - TestSpec m_testSpec; - ITagAliasRegistry const* m_tagAliases = nullptr; - - public: - TestSpecParser( ITagAliasRegistry const& tagAliases ); - - TestSpecParser& parse( std::string const& arg ); - TestSpec testSpec(); - - private: - void visitChar( char c ); - void startNewMode( Mode mode, std::size_t start ); - void escape(); - std::string subString() const; - - template - void addPattern() { - std::string token = subString(); - for( std::size_t i = 0; i < m_escapeChars.size(); ++i ) - token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 ); - m_escapeChars.clear(); - if( startsWith( token, "exclude:" ) ) { - m_exclusion = true; - token = token.substr( 8 ); - } - if( !token.empty() ) { - TestSpec::PatternPtr pattern = std::make_shared( token ); - if( m_exclusion ) - pattern = std::make_shared( pattern ); - m_currentFilter.m_patterns.push_back( pattern ); - } - m_exclusion = false; - m_mode = None; - } - - void addFilter(); + class TestSpecParser { + enum Mode { + None, + Name, + QuotedName, + Tag, + EscapedName }; - TestSpec parseTestSpec( std::string const& arg ); + Mode m_mode = None; + Mode lastMode = None; + bool m_exclusion = false; + std::size_t m_pos = 0; + std::size_t m_realPatternPos = 0; + std::string m_arg; + std::string m_substring; + std::string m_patternName; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const *m_tagAliases = nullptr; + + public: + TestSpecParser(ITagAliasRegistry const &tagAliases); + + TestSpecParser &parse(std::string const &arg); + TestSpec testSpec(); + + private: + bool visitChar(char c); + void startNewMode(Mode mode); + bool processNoneChar(char c); + void processNameChar(char c); + bool processOtherChar(char c); + void endMode(); + void escape(); + bool isControlChar(char c) const; + void saveLastMode(); + void revertBackToLastMode(); + void addFilter(); + bool separate(); + + // Handles common preprocessing of the pattern for name/tag patterns + std::string preprocessPattern(); + // Adds the current pattern as a test name + void addNamePattern(); + // Adds the current pattern as a tag + void addTagPattern(); + + inline void addCharToPattern(char c) { + m_substring += c; + m_patternName += c; + m_realPatternPos++; + } + }; + TestSpec parseTestSpec(std::string const &arg); } // namespace Catch @@ -3239,178 +5614,117 @@ namespace Catch { #endif // end catch_test_spec_parser.h -// start catch_interfaces_config.h - -#include -#include -#include -#include - -namespace Catch { - - enum class Verbosity { - Quiet = 0, - Normal, - High - }; - - struct WarnAbout { enum What { - Nothing = 0x00, - NoAssertions = 0x01, - NoTests = 0x02 - }; }; - - struct ShowDurations { enum OrNot { - DefaultForReporter, - Always, - Never - }; }; - struct RunTests { enum InWhatOrder { - InDeclarationOrder, - InLexicographicalOrder, - InRandomOrder - }; }; - struct UseColour { enum YesOrNo { - Auto, - Yes, - No - }; }; - struct WaitForKeypress { enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; }; - - class TestSpec; - - struct IConfig : NonCopyable { - - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual std::ostream& stream() const = 0; - virtual std::string name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutNoTests() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations::OrNot showDurations() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual RunTests::InWhatOrder runOrder() const = 0; - virtual unsigned int rngSeed() const = 0; - virtual int benchmarkResolutionMultiple() const = 0; - virtual UseColour::YesOrNo useColour() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - }; - - using IConfigPtr = std::shared_ptr; -} - -// end catch_interfaces_config.h // Libstdc++ doesn't like incomplete classes for unique_ptr #include -#include #include +#include #ifndef CATCH_CONFIG_CONSOLE_WIDTH #define CATCH_CONFIG_CONSOLE_WIDTH 80 #endif namespace Catch { + struct IStream; - struct IStream; + struct ConfigData { + bool listTests = false; + bool listTags = false; + bool listReporters = false; + bool listTestNamesOnly = false; - struct ConfigData { - bool listTests = false; - bool listTags = false; - bool listReporters = false; - bool listTestNamesOnly = false; + bool showSuccessfulTests = false; + bool shouldDebugBreak = false; + bool noThrow = false; + bool showHelp = false; + bool showInvisibles = false; + bool filenamesAsTags = false; + bool libIdentify = false; - bool showSuccessfulTests = false; - bool shouldDebugBreak = false; - bool noThrow = false; - bool showHelp = false; - bool showInvisibles = false; - bool filenamesAsTags = false; - bool libIdentify = false; + int abortAfter = -1; + unsigned int rngSeed = 0; - int abortAfter = -1; - unsigned int rngSeed = 0; - int benchmarkResolutionMultiple = 100; + bool benchmarkNoAnalysis = false; + unsigned int benchmarkSamples = 100; + double benchmarkConfidenceInterval = 0.95; + unsigned int benchmarkResamples = 100000; + std::chrono::milliseconds::rep benchmarkWarmupTime = 100; - Verbosity verbosity = Verbosity::Normal; - WarnAbout::What warnings = WarnAbout::Nothing; - ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; - RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; - UseColour::YesOrNo useColour = UseColour::Auto; - WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; + Verbosity verbosity = Verbosity::Normal; + WarnAbout::What warnings = WarnAbout::Nothing; + ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter; + double minDuration = -1; + RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder; + UseColour::YesOrNo useColour = UseColour::Auto; + WaitForKeypress::When waitForKeypress = WaitForKeypress::Never; - std::string outputFilename; - std::string name; - std::string processName; + std::string outputFilename; + std::string name; + std::string processName; +#ifndef CATCH_CONFIG_DEFAULT_REPORTER +#define CATCH_CONFIG_DEFAULT_REPORTER "console" +#endif + std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER; +#undef CATCH_CONFIG_DEFAULT_REPORTER - std::vector reporterNames; - std::vector testsOrTags; - std::vector sectionsToRun; - }; + std::vector testsOrTags; + std::vector sectionsToRun; + }; - class Config : public IConfig { - public: + class Config : public IConfig { + public: + Config() = default; + Config(ConfigData const &data); + virtual ~Config() = default; - Config() = default; - Config( ConfigData const& data ); - virtual ~Config() = default; + std::string const &getFilename() const; - std::string const& getFilename() const; + bool listTests() const; + bool listTestNamesOnly() const; + bool listTags() const; + bool listReporters() const; - bool listTests() const; - bool listTestNamesOnly() const; - bool listTags() const; - bool listReporters() const; + std::string getProcessName() const; + std::string const &getReporterName() const; - std::string getProcessName() const; + std::vector const &getTestsOrTags() const override; + std::vector const &getSectionsToRun() const override; - std::vector const& getReporterNames() const; - std::vector const& getTestsOrTags() const; - std::vector const& getSectionsToRun() const override; + TestSpec const &testSpec() const override; + bool hasTestFilters() const override; - virtual TestSpec const& testSpec() const override; - bool hasTestFilters() const override; + bool showHelp() const; - bool showHelp() const; + // IConfig interface + bool allowThrows() const override; + std::ostream &stream() const override; + std::string name() const override; + bool includeSuccessfulResults() const override; + bool warnAboutMissingAssertions() const override; + bool warnAboutNoTests() const override; + ShowDurations::OrNot showDurations() const override; + double minDuration() const override; + RunTests::InWhatOrder runOrder() const override; + unsigned int rngSeed() const override; + UseColour::YesOrNo useColour() const override; + bool shouldDebugBreak() const override; + int abortAfter() const override; + bool showInvisibles() const override; + Verbosity verbosity() const override; + bool benchmarkNoAnalysis() const override; + int benchmarkSamples() const override; + double benchmarkConfidenceInterval() const override; + unsigned int benchmarkResamples() const override; + std::chrono::milliseconds benchmarkWarmupTime() const override; - // IConfig interface - bool allowThrows() const override; - std::ostream& stream() const override; - std::string name() const override; - bool includeSuccessfulResults() const override; - bool warnAboutMissingAssertions() const override; - bool warnAboutNoTests() const override; - ShowDurations::OrNot showDurations() const override; - RunTests::InWhatOrder runOrder() const override; - unsigned int rngSeed() const override; - int benchmarkResolutionMultiple() const override; - UseColour::YesOrNo useColour() const override; - bool shouldDebugBreak() const override; - int abortAfter() const override; - bool showInvisibles() const override; - Verbosity verbosity() const override; + private: + IStream const *openStream(); + ConfigData m_data; - private: - - IStream const* openStream(); - ConfigData m_data; - - std::unique_ptr m_stream; - TestSpec m_testSpec; - bool m_hasTestFilters = false; - }; + std::unique_ptr m_stream; + TestSpec m_testSpec; + bool m_hasTestFilters = false; + }; } // end namespace Catch @@ -3420,580 +5734,557 @@ namespace Catch { #include namespace Catch { + struct AssertionResultData { + AssertionResultData() = delete; - struct AssertionResultData - { - AssertionResultData() = delete; + AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression); - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; + std::string reconstructExpression() const; + }; - std::string reconstructExpression() const; - }; + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult(AssertionInfo const &info, AssertionResultData const &data); - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - std::string getMessage() const; - SourceLineInfo getSourceInfo() const; - StringRef getTestMacroName() const; - - //protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; + // protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch // end catch_assertionresult.h -// start catch_option.hpp +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_estimate.hpp + +// Statistics estimates namespace Catch { + namespace Benchmark { + template + struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; - // An optional type - template - class Option { - public: - Option() : nullableValue( nullptr ) {} - Option( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Option( Option const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) - {} - - ~Option() { - reset(); - } - - Option& operator= ( Option const& _other ) { - if( &_other != this ) { - reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); - } - return *this; - } - Option& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); - return *this; - } - - void reset() { - if( nullableValue ) - nullableValue->~T(); - nullableValue = nullptr; - } - - T& operator*() { return *nullableValue; } - T const& operator*() const { return *nullableValue; } - T* operator->() { return nullableValue; } - const T* operator->() const { return nullableValue; } - - T valueOr( T const& defaultValue ) const { - return nullableValue ? *nullableValue : defaultValue; - } - - bool some() const { return nullableValue != nullptr; } - bool none() const { return nullableValue == nullptr; } - - bool operator !() const { return nullableValue == nullptr; } - explicit operator bool() const { - return some(); - } - - private: - T *nullableValue; - alignas(alignof(T)) char storage[sizeof(T)]; + template + operator Estimate() const { + return {point, lower_bound, upper_bound, confidence_interval}; + } }; + } // namespace Benchmark +} // namespace Catch -} // end namespace Catch +// end catch_estimate.hpp +// start catch_outlier_classification.hpp -// end catch_option.hpp -#include +// Outlier information + +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const { return low_severe + low_mild + high_mild + high_severe; } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_outlier_classification.hpp +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + +#include #include #include -#include #include +#include +#include namespace Catch { + struct ReporterConfig { + explicit ReporterConfig(IConfigPtr const &_fullConfig); - struct ReporterConfig { - explicit ReporterConfig( IConfigPtr const& _fullConfig ); + ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream); - ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ); + std::ostream &stream() const; + IConfigPtr fullConfig() const; - std::ostream& stream() const; - IConfigPtr fullConfig() const; + private: + std::ostream *m_stream; + IConfigPtr m_fullConfig; + }; - private: - std::ostream* m_stream; - IConfigPtr m_fullConfig; - }; + struct ReporterPreferences { + bool shouldRedirectStdOut = false; + bool shouldReportAllAssertions = false; + }; - struct ReporterPreferences { - bool shouldRedirectStdOut = false; - }; + template + struct LazyStat : Option { + LazyStat &operator=(T const &_value) { + Option::operator=(_value); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used = false; + }; - template - struct LazyStat : Option { - LazyStat& operator=( T const& _value ) { - Option::operator=( _value ); - used = false; - return *this; - } - void reset() { - Option::reset(); - used = false; - } - bool used = false; - }; + struct TestRunInfo { + TestRunInfo(std::string const &_name); + std::string name; + }; + struct GroupInfo { + GroupInfo(std::string const &_name, std::size_t _groupIndex, std::size_t _groupsCount); - struct TestRunInfo { - TestRunInfo( std::string const& _name ); - std::string name; - }; - struct GroupInfo { - GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ); + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; - std::string name; - std::size_t groupIndex; - std::size_t groupsCounts; - }; + struct AssertionStats { + AssertionStats(AssertionResult const &_assertionResult, std::vector const &_infoMessages, Totals const &_totals); - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ); + AssertionStats(AssertionStats const &) = default; + AssertionStats(AssertionStats &&) = default; + AssertionStats &operator=(AssertionStats const &) = delete; + AssertionStats &operator=(AssertionStats &&) = delete; + virtual ~AssertionStats(); - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = default; - AssertionStats& operator = ( AssertionStats && ) = default; - virtual ~AssertionStats(); + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; - }; + struct SectionStats { + SectionStats(SectionInfo const &_sectionInfo, Counts const &_assertions, double _durationInSeconds, bool _missingAssertions); + SectionStats(SectionStats const &) = default; + SectionStats(SectionStats &&) = default; + SectionStats &operator=(SectionStats const &) = default; + SectionStats &operator=(SectionStats &&) = default; + virtual ~SectionStats(); - struct SectionStats { - SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ); - SectionStats( SectionStats const& ) = default; - SectionStats( SectionStats && ) = default; - SectionStats& operator = ( SectionStats const& ) = default; - SectionStats& operator = ( SectionStats && ) = default; - virtual ~SectionStats(); + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; - }; + struct TestCaseStats { + TestCaseStats(TestCaseInfo const &_testInfo, Totals const &_totals, std::string const &_stdOut, std::string const &_stdErr, bool _aborting); - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ); + TestCaseStats(TestCaseStats const &) = default; + TestCaseStats(TestCaseStats &&) = default; + TestCaseStats &operator=(TestCaseStats const &) = default; + TestCaseStats &operator=(TestCaseStats &&) = default; + virtual ~TestCaseStats(); - TestCaseStats( TestCaseStats const& ) = default; - TestCaseStats( TestCaseStats && ) = default; - TestCaseStats& operator = ( TestCaseStats const& ) = default; - TestCaseStats& operator = ( TestCaseStats && ) = default; - virtual ~TestCaseStats(); + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; - TestCaseInfo testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; - }; + struct TestGroupStats { + TestGroupStats(GroupInfo const &_groupInfo, Totals const &_totals, bool _aborting); + TestGroupStats(GroupInfo const &_groupInfo); - struct TestGroupStats { - TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ); - TestGroupStats( GroupInfo const& _groupInfo ); + TestGroupStats(TestGroupStats const &) = default; + TestGroupStats(TestGroupStats &&) = default; + TestGroupStats &operator=(TestGroupStats const &) = default; + TestGroupStats &operator=(TestGroupStats &&) = default; + virtual ~TestGroupStats(); - TestGroupStats( TestGroupStats const& ) = default; - TestGroupStats( TestGroupStats && ) = default; - TestGroupStats& operator = ( TestGroupStats const& ) = default; - TestGroupStats& operator = ( TestGroupStats && ) = default; - virtual ~TestGroupStats(); + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; - GroupInfo groupInfo; - Totals totals; - bool aborting; - }; + struct TestRunStats { + TestRunStats(TestRunInfo const &_runInfo, Totals const &_totals, bool _aborting); - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ); + TestRunStats(TestRunStats const &) = default; + TestRunStats(TestRunStats &&) = default; + TestRunStats &operator=(TestRunStats const &) = default; + TestRunStats &operator=(TestRunStats &&) = default; + virtual ~TestRunStats(); - TestRunStats( TestRunStats const& ) = default; - TestRunStats( TestRunStats && ) = default; - TestRunStats& operator = ( TestRunStats const& ) = default; - TestRunStats& operator = ( TestRunStats && ) = default; - virtual ~TestRunStats(); + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; - TestRunInfo runInfo; - Totals totals; - bool aborting; - }; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + struct BenchmarkInfo { + std::string name; + double estimatedDuration; + int iterations; + int samples; + unsigned int resamples; + double clockResolution; + double clockCost; + }; - struct BenchmarkInfo { - std::string name; - }; - struct BenchmarkStats { - BenchmarkInfo info; - std::size_t iterations; - uint64_t elapsedTimeInNanoseconds; - }; + template + struct BenchmarkStats { + BenchmarkInfo info; - struct IStreamingReporter { - virtual ~IStreamingReporter() = default; + std::vector samples; + Benchmark::Estimate mean; + Benchmark::Estimate standardDeviation; + Benchmark::OutlierClassification outliers; + double outlierVariance; - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - // static std::set getSupportedVerbosities() + template + operator BenchmarkStats() const { + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + info, + std::move(samples2), + mean, + standardDeviation, + outliers, + outlierVariance, + }; + } + }; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - virtual ReporterPreferences getPreferences() const = 0; + struct IStreamingReporter { + virtual ~IStreamingReporter() = default; - virtual void noMatchingTestCases( std::string const& spec ) = 0; + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + // static std::set getSupportedVerbosities() - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + virtual ReporterPreferences getPreferences() const = 0; - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + virtual void noMatchingTestCases(std::string const &spec) = 0; - // *** experimental *** - virtual void benchmarkStarting( BenchmarkInfo const& ) {} + virtual void reportInvalidArguments(std::string const &) {} - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + virtual void testRunStarting(TestRunInfo const &testRunInfo) = 0; + virtual void testGroupStarting(GroupInfo const &groupInfo) = 0; - // The return value indicates if the messages buffer should be cleared: - virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + virtual void testCaseStarting(TestCaseInfo const &testInfo) = 0; + virtual void sectionStarting(SectionInfo const §ionInfo) = 0; - // *** experimental *** - virtual void benchmarkEnded( BenchmarkStats const& ) {} +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + virtual void benchmarkPreparing(std::string const &) { + } + virtual void benchmarkStarting(BenchmarkInfo const &) {} + virtual void benchmarkEnded(BenchmarkStats<> const &) {} + virtual void benchmarkFailed(std::string const &) {} +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + virtual void assertionStarting(AssertionInfo const &assertionInfo) = 0; - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded(AssertionStats const &assertionStats) = 0; - // Default empty implementation provided - virtual void fatalErrorEncountered( StringRef name ); + virtual void sectionEnded(SectionStats const §ionStats) = 0; + virtual void testCaseEnded(TestCaseStats const &testCaseStats) = 0; + virtual void testGroupEnded(TestGroupStats const &testGroupStats) = 0; + virtual void testRunEnded(TestRunStats const &testRunStats) = 0; - virtual bool isMulti() const; - }; - using IStreamingReporterPtr = std::unique_ptr; + virtual void skipTest(TestCaseInfo const &testInfo) = 0; - struct IReporterFactory { - virtual ~IReporterFactory(); - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0; - virtual std::string getDescription() const = 0; - }; - using IReporterFactoryPtr = std::shared_ptr; + // Default empty implementation provided + virtual void fatalErrorEncountered(StringRef name); - struct IReporterRegistry { - using FactoryMap = std::map; - using Listeners = std::vector; + virtual bool isMulti() const; + }; + using IStreamingReporterPtr = std::unique_ptr; - virtual ~IReporterRegistry(); - virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; - }; + struct IReporterFactory { + virtual ~IReporterFactory(); + virtual IStreamingReporterPtr create(ReporterConfig const &config) const = 0; + virtual std::string getDescription() const = 0; + }; + using IReporterFactoryPtr = std::shared_ptr; - void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ); + struct IReporterRegistry { + using FactoryMap = std::map; + using Listeners = std::vector; + + virtual ~IReporterRegistry(); + virtual IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const = 0; + virtual FactoryMap const &getFactories() const = 0; + virtual Listeners const &getListeners() const = 0; + }; } // end namespace Catch // end catch_interfaces_reporter.h #include -#include +#include #include #include -#include +#include #include #include namespace Catch { - void prepareExpandedExpression(AssertionResult& result); + void prepareExpandedExpression(AssertionResult &result); - // Returns double formatted as %.3f (format expected on output) - std::string getFormattedDuration( double duration ); + // Returns double formatted as %.3f (format expected on output) + std::string getFormattedDuration(double duration); - template - struct StreamingReporterBase : IStreamingReporter { + //! Should the reporter show + bool shouldShowDuration(IConfig const &config, double duration); - StreamingReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) - throw std::domain_error( "Verbosity level not supported by this reporter" ); - } + std::string serializeFilters(std::vector const &container); - ReporterPreferences getPreferences() const override { - return m_reporterPrefs; - } - - static std::set getSupportedVerbosities() { - return { Verbosity::Normal }; - } - - ~StreamingReporterBase() override = default; - - void noMatchingTestCases(std::string const&) override {} - - void testRunStarting(TestRunInfo const& _testRunInfo) override { - currentTestRunInfo = _testRunInfo; - } - void testGroupStarting(GroupInfo const& _groupInfo) override { - currentGroupInfo = _groupInfo; - } - - void testCaseStarting(TestCaseInfo const& _testInfo) override { - currentTestCaseInfo = _testInfo; - } - void sectionStarting(SectionInfo const& _sectionInfo) override { - m_sectionStack.push_back(_sectionInfo); - } - - void sectionEnded(SectionStats const& /* _sectionStats */) override { - m_sectionStack.pop_back(); - } - void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override { - currentTestCaseInfo.reset(); - } - void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override { - currentGroupInfo.reset(); - } - void testRunEnded(TestRunStats const& /* _testRunStats */) override { - currentTestCaseInfo.reset(); - currentGroupInfo.reset(); - currentTestRunInfo.reset(); - } - - void skipTest(TestCaseInfo const&) override { - // Don't do anything with this by default. - // It can optionally be overridden in the derived class. - } - - IConfigPtr m_config; - std::ostream& stream; - - LazyStat currentTestRunInfo; - LazyStat currentGroupInfo; - LazyStat currentTestCaseInfo; - - std::vector m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - template - struct CumulativeReporterBase : IStreamingReporter { - template - struct Node { - explicit Node( T const& _value ) : value( _value ) {} - virtual ~Node() {} - - using ChildNodes = std::vector>; - T value; - ChildNodes children; - }; - struct SectionNode { - explicit SectionNode(SectionStats const& _stats) : stats(_stats) {} - virtual ~SectionNode() = default; - - bool operator == (SectionNode const& other) const { - return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; - } - bool operator == (std::shared_ptr const& other) const { - return operator==(*other); - } - - SectionStats stats; - using ChildSections = std::vector>; - using Assertions = std::vector; - ChildSections childSections; - Assertions assertions; - std::string stdOut; - std::string stdErr; - }; - - struct BySectionInfo { - BySectionInfo( SectionInfo const& other ) : m_other( other ) {} - BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} - bool operator() (std::shared_ptr const& node) const { - return ((node->stats.sectionInfo.name == m_other.name) && - (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); - } - void operator=(BySectionInfo const&) = delete; - - private: - SectionInfo const& m_other; - }; - - using TestCaseNode = Node; - using TestGroupNode = Node; - using TestRunNode = Node; - - CumulativeReporterBase( ReporterConfig const& _config ) - : m_config( _config.fullConfig() ), - stream( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = false; - if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) ) - throw std::domain_error( "Verbosity level not supported by this reporter" ); - } - ~CumulativeReporterBase() override = default; - - ReporterPreferences getPreferences() const override { - return m_reporterPrefs; - } - - static std::set getSupportedVerbosities() { - return { Verbosity::Normal }; - } - - void testRunStarting( TestRunInfo const& ) override {} - void testGroupStarting( GroupInfo const& ) override {} - - void testCaseStarting( TestCaseInfo const& ) override {} - - void sectionStarting( SectionInfo const& sectionInfo ) override { - SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); - std::shared_ptr node; - if( m_sectionStack.empty() ) { - if( !m_rootSection ) - m_rootSection = std::make_shared( incompleteStats ); - node = m_rootSection; - } - else { - SectionNode& parentNode = *m_sectionStack.back(); - auto it = - std::find_if( parentNode.childSections.begin(), - parentNode.childSections.end(), - BySectionInfo( sectionInfo ) ); - if( it == parentNode.childSections.end() ) { - node = std::make_shared( incompleteStats ); - parentNode.childSections.push_back( node ); - } - else - node = *it; - } - m_sectionStack.push_back( node ); - m_deepestSection = std::move(node); - } - - void assertionStarting(AssertionInfo const&) override {} - - bool assertionEnded(AssertionStats const& assertionStats) override { - assert(!m_sectionStack.empty()); - // AssertionResult holds a pointer to a temporary DecomposedExpression, - // which getExpandedExpression() calls to build the expression string. - // Our section stack copy of the assertionResult will likely outlive the - // temporary, so it must be expanded or discarded now to avoid calling - // a destroyed object later. - prepareExpandedExpression(const_cast( assertionStats.assertionResult ) ); - SectionNode& sectionNode = *m_sectionStack.back(); - sectionNode.assertions.push_back(assertionStats); - return true; - } - void sectionEnded(SectionStats const& sectionStats) override { - assert(!m_sectionStack.empty()); - SectionNode& node = *m_sectionStack.back(); - node.stats = sectionStats; - m_sectionStack.pop_back(); - } - void testCaseEnded(TestCaseStats const& testCaseStats) override { - auto node = std::make_shared(testCaseStats); - assert(m_sectionStack.size() == 0); - node->children.push_back(m_rootSection); - m_testCases.push_back(node); - m_rootSection.reset(); - - assert(m_deepestSection); - m_deepestSection->stdOut = testCaseStats.stdOut; - m_deepestSection->stdErr = testCaseStats.stdErr; - } - void testGroupEnded(TestGroupStats const& testGroupStats) override { - auto node = std::make_shared(testGroupStats); - node->children.swap(m_testCases); - m_testGroups.push_back(node); - } - void testRunEnded(TestRunStats const& testRunStats) override { - auto node = std::make_shared(testRunStats); - node->children.swap(m_testGroups); - m_testRuns.push_back(node); - testRunEndedCumulative(); - } - virtual void testRunEndedCumulative() = 0; - - void skipTest(TestCaseInfo const&) override {} - - IConfigPtr m_config; - std::ostream& stream; - std::vector m_assertions; - std::vector>> m_sections; - std::vector> m_testCases; - std::vector> m_testGroups; - - std::vector> m_testRuns; - - std::shared_ptr m_rootSection; - std::shared_ptr m_deepestSection; - std::vector> m_sectionStack; - ReporterPreferences m_reporterPrefs; - }; - - template - char const* getLineOfChars() { - static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; - if( !*line ) { - std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); - line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; - } - return line; + template + struct StreamingReporterBase : IStreamingReporter { + StreamingReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()) + , stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) + CATCH_ERROR("Verbosity level not supported by this reporter"); } - struct TestEventListenerBase : StreamingReporterBase { - TestEventListenerBase( ReporterConfig const& _config ); + ReporterPreferences getPreferences() const override { return m_reporterPrefs; } - void assertionStarting(AssertionInfo const&) override; - bool assertionEnded(AssertionStats const&) override; + static std::set getSupportedVerbosities() { return {Verbosity::Normal}; } + + ~StreamingReporterBase() override = default; + + void noMatchingTestCases(std::string const &) override {} + + void reportInvalidArguments(std::string const &) override {} + + void testRunStarting(TestRunInfo const &_testRunInfo) override { currentTestRunInfo = _testRunInfo; } + + void testGroupStarting(GroupInfo const &_groupInfo) override { currentGroupInfo = _groupInfo; } + + void testCaseStarting(TestCaseInfo const &_testInfo) override { currentTestCaseInfo = _testInfo; } + void sectionStarting(SectionInfo const &_sectionInfo) override { m_sectionStack.push_back(_sectionInfo); } + + void sectionEnded(SectionStats const & /* _sectionStats */) override { m_sectionStack.pop_back(); } + void testCaseEnded(TestCaseStats const & /* _testCaseStats */) override { currentTestCaseInfo.reset(); } + void testGroupEnded(TestGroupStats const & /* _testGroupStats */) override { currentGroupInfo.reset(); } + void testRunEnded(TestRunStats const & /* _testRunStats */) override { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + void skipTest(TestCaseInfo const &) override { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + IConfigPtr m_config; + std::ostream &stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + struct CumulativeReporterBase : IStreamingReporter { + template + struct Node { + explicit Node(T const &_value) + : value(_value) { + } + virtual ~Node() {} + + using ChildNodes = std::vector>; + T value; + ChildNodes children; }; + struct SectionNode { + explicit SectionNode(SectionStats const &_stats) + : stats(_stats) { + } + virtual ~SectionNode() = default; + + bool operator==(SectionNode const &other) const { return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; } + bool operator==(std::shared_ptr const &other) const { return operator==(*other); } + + SectionStats stats; + using ChildSections = std::vector>; + using Assertions = std::vector; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo(SectionInfo const &other) + : m_other(other) { + } + BySectionInfo(BySectionInfo const &other) + : m_other(other.m_other) { + } + bool operator()(std::shared_ptr const &node) const { + return ((node->stats.sectionInfo.name == m_other.name) && (node->stats.sectionInfo.lineInfo == m_other.lineInfo)); + } + void operator=(BySectionInfo const &) = delete; + + private: + SectionInfo const &m_other; + }; + + using TestCaseNode = Node; + using TestGroupNode = Node; + using TestRunNode = Node; + + CumulativeReporterBase(ReporterConfig const &_config) + : m_config(_config.fullConfig()) + , stream(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = false; + if (!DerivedT::getSupportedVerbosities().count(m_config->verbosity())) + CATCH_ERROR("Verbosity level not supported by this reporter"); + } + ~CumulativeReporterBase() override = default; + + ReporterPreferences getPreferences() const override { return m_reporterPrefs; } + + static std::set getSupportedVerbosities() { return {Verbosity::Normal}; } + + void testRunStarting(TestRunInfo const &) override {} + void testGroupStarting(GroupInfo const &) override {} + + void testCaseStarting(TestCaseInfo const &) override {} + + void sectionStarting(SectionInfo const §ionInfo) override { + SectionStats incompleteStats(sectionInfo, Counts(), 0, false); + std::shared_ptr node; + if (m_sectionStack.empty()) { + if (!m_rootSection) + m_rootSection = std::make_shared(incompleteStats); + node = m_rootSection; + } else { + SectionNode &parentNode = *m_sectionStack.back(); + auto it = std::find_if(parentNode.childSections.begin(), parentNode.childSections.end(), BySectionInfo(sectionInfo)); + if (it == parentNode.childSections.end()) { + node = std::make_shared(incompleteStats); + parentNode.childSections.push_back(node); + } else + node = *it; + } + m_sectionStack.push_back(node); + m_deepestSection = std::move(node); + } + + void assertionStarting(AssertionInfo const &) override {} + + bool assertionEnded(AssertionStats const &assertionStats) override { + assert(!m_sectionStack.empty()); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression(const_cast(assertionStats.assertionResult)); + SectionNode §ionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back(assertionStats); + return true; + } + void sectionEnded(SectionStats const §ionStats) override { + assert(!m_sectionStack.empty()); + SectionNode &node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + void testCaseEnded(TestCaseStats const &testCaseStats) override { + auto node = std::make_shared(testCaseStats); + assert(m_sectionStack.size() == 0); + node->children.push_back(m_rootSection); + m_testCases.push_back(node); + m_rootSection.reset(); + + assert(m_deepestSection); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + void testGroupEnded(TestGroupStats const &testGroupStats) override { + auto node = std::make_shared(testGroupStats); + node->children.swap(m_testCases); + m_testGroups.push_back(node); + } + void testRunEnded(TestRunStats const &testRunStats) override { + auto node = std::make_shared(testRunStats); + node->children.swap(m_testGroups); + m_testRuns.push_back(node); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + void skipTest(TestCaseInfo const &) override {} + + IConfigPtr m_config; + std::ostream &stream; + std::vector m_assertions; + std::vector>> m_sections; + std::vector> m_testCases; + std::vector> m_testGroups; + + std::vector> m_testRuns; + + std::shared_ptr m_rootSection; + std::shared_ptr m_deepestSection; + std::vector> m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + template + char const *getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if (!*line) { + std::memset(line, C, CATCH_CONFIG_CONSOLE_WIDTH - 1); + line[CATCH_CONFIG_CONSOLE_WIDTH - 1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase(ReporterConfig const &_config); + + static std::set getSupportedVerbosities(); + + void assertionStarting(AssertionInfo const &) override; + bool assertionEnded(AssertionStats const &) override; + }; } // end namespace Catch @@ -4001,120 +6292,107 @@ namespace Catch { // start catch_console_colour.h namespace Catch { + struct Colour { + enum Code { + None = 0, - struct Colour { - enum Code { - None = 0, + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, - White, - Red, - Green, - Blue, - Cyan, - Yellow, - Grey, + Bright = 0x10, - Bright = 0x10, + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + BrightYellow = Bright | Yellow, - BrightRed = Bright | Red, - BrightGreen = Bright | Green, - LightGrey = Bright | Grey, - BrightWhite = Bright | White, - BrightYellow = Bright | Yellow, + // By intention + FileName = LightGrey, + Warning = BrightYellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, - // By intention - FileName = LightGrey, - Warning = BrightYellow, - ResultError = BrightRed, - ResultSuccess = BrightGreen, - ResultExpectedFailure = Warning, + Error = BrightRed, + Success = Green, - Error = BrightRed, - Success = Green, + OriginalExpression = Cyan, + ReconstructedExpression = BrightYellow, - OriginalExpression = Cyan, - ReconstructedExpression = BrightYellow, - - SecondaryText = LightGrey, - Headers = White - }; - - // Use constructed object for RAII guard - Colour( Code _colourCode ); - Colour( Colour&& other ) noexcept; - Colour& operator=( Colour&& other ) noexcept; - ~Colour(); - - // Use static method for one-shot changes - static void use( Code _colourCode ); - - private: - bool m_moved = false; + SecondaryText = LightGrey, + Headers = White }; - std::ostream& operator << ( std::ostream& os, Colour const& ); + // Use constructed object for RAII guard + Colour(Code _colourCode); + Colour(Colour &&other) noexcept; + Colour &operator=(Colour &&other) noexcept; + ~Colour(); + + // Use static method for one-shot changes + static void use(Code _colourCode); + + private: + bool m_moved = false; + }; + + std::ostream &operator<<(std::ostream &os, Colour const &); } // end namespace Catch // end catch_console_colour.h // start catch_reporter_registrars.hpp - namespace Catch { + template + class ReporterRegistrar { + class ReporterFactory : public IReporterFactory { + IStreamingReporterPtr create(ReporterConfig const &config) const override { return std::unique_ptr(new T(config)); } - template - class ReporterRegistrar { - - class ReporterFactory : public IReporterFactory { - - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { - return std::unique_ptr( new T( config ) ); - } - - virtual std::string getDescription() const override { - return T::getDescription(); - } - }; - - public: - - explicit ReporterRegistrar( std::string const& name ) { - getMutableRegistryHub().registerReporter( name, std::make_shared() ); - } + std::string getDescription() const override { return T::getDescription(); } }; - template - class ListenerRegistrar { + public: + explicit ReporterRegistrar(std::string const &name) { + getMutableRegistryHub().registerReporter(name, std::make_shared()); + } + }; - class ListenerFactory : public IReporterFactory { - - virtual IStreamingReporterPtr create( ReporterConfig const& config ) const override { - return std::unique_ptr( new T( config ) ); - } - virtual std::string getDescription() const override { - return std::string(); - } - }; - - public: - - ListenerRegistrar() { - getMutableRegistryHub().registerListener( std::make_shared() ); - } + template + class ListenerRegistrar { + class ListenerFactory : public IReporterFactory { + IStreamingReporterPtr create(ReporterConfig const &config) const override { return std::unique_ptr(new T(config)); } + std::string getDescription() const override { return std::string(); } }; -} + + public: + ListenerRegistrar() { getMutableRegistryHub().registerListener(std::make_shared()); } + }; +} // namespace Catch #if !defined(CATCH_CONFIG_DISABLE) -#define CATCH_REGISTER_REPORTER( name, reporterType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } \ - CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS +#define CATCH_REGISTER_REPORTER(name, reporterType) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType(name); \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#define CATCH_REGISTER_LISTENER( listenerType ) \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#define CATCH_REGISTER_LISTENER(listenerType) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace { \ + Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; \ + } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #else // CATCH_CONFIG_DISABLE #define CATCH_REGISTER_REPORTER(name, reporterType) @@ -4127,28 +6405,23 @@ namespace Catch { // start catch_reporter_compact.h namespace Catch { + struct CompactReporter : StreamingReporterBase { + using StreamingReporterBase::StreamingReporterBase; - struct CompactReporter : StreamingReporterBase { + ~CompactReporter() override; - using StreamingReporterBase::StreamingReporterBase; + static std::string getDescription(); - ~CompactReporter() override; + void noMatchingTestCases(std::string const &spec) override; - static std::string getDescription(); + void assertionStarting(AssertionInfo const &) override; - ReporterPreferences getPreferences() const override; + bool assertionEnded(AssertionStats const &_assertionStats) override; - void noMatchingTestCases(std::string const& spec) override; + void sectionEnded(SectionStats const &_sectionStats) override; - void assertionStarting(AssertionInfo const&) override; - - bool assertionEnded(AssertionStats const& _assertionStats) override; - - void sectionEnded(SectionStats const& _sectionStats) override; - - void testRunEnded(TestRunStats const& _testRunStats) override; - - }; + void testRunEnded(TestRunStats const &_testRunStats) override; + }; } // end namespace Catch @@ -4157,64 +6430,71 @@ namespace Catch { #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled - // and default is missing) is enabled +#pragma warning(disable : 4061) // Not all labels are EXPLICITLY handled in switch \ + // Note that 4062 (not all labels are handled \ + // and default is missing) is enabled #endif namespace Catch { - // Fwd decls - struct SummaryColumn; - class TablePrinter; + // Fwd decls + struct SummaryColumn; + class TablePrinter; - struct ConsoleReporter : StreamingReporterBase { - std::unique_ptr m_tablePrinter; + struct ConsoleReporter : StreamingReporterBase { + std::unique_ptr m_tablePrinter; - ConsoleReporter(ReporterConfig const& config); - ~ConsoleReporter() override; - static std::string getDescription(); + ConsoleReporter(ReporterConfig const &config); + ~ConsoleReporter() override; + static std::string getDescription(); - void noMatchingTestCases(std::string const& spec) override; + void noMatchingTestCases(std::string const &spec) override; - void assertionStarting(AssertionInfo const&) override; + void reportInvalidArguments(std::string const &arg) override; - bool assertionEnded(AssertionStats const& _assertionStats) override; + void assertionStarting(AssertionInfo const &) override; - void sectionStarting(SectionInfo const& _sectionInfo) override; - void sectionEnded(SectionStats const& _sectionStats) override; + bool assertionEnded(AssertionStats const &_assertionStats) override; - void benchmarkStarting(BenchmarkInfo const& info) override; - void benchmarkEnded(BenchmarkStats const& stats) override; + void sectionStarting(SectionInfo const &_sectionInfo) override; + void sectionEnded(SectionStats const &_sectionStats) override; - void testCaseEnded(TestCaseStats const& _testCaseStats) override; - void testGroupEnded(TestGroupStats const& _testGroupStats) override; - void testRunEnded(TestRunStats const& _testRunStats) override; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &info) override; + void benchmarkEnded(BenchmarkStats<> const &stats) override; + void benchmarkFailed(std::string const &error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - private: + void testCaseEnded(TestCaseStats const &_testCaseStats) override; + void testGroupEnded(TestGroupStats const &_testGroupStats) override; + void testRunEnded(TestRunStats const &_testRunStats) override; + void testRunStarting(TestRunInfo const &_testRunInfo) override; - void lazyPrint(); + private: + void lazyPrint(); - void lazyPrintWithoutClosingBenchmarkTable(); - void lazyPrintRunInfo(); - void lazyPrintGroupInfo(); - void printTestCaseAndSectionHeader(); + void lazyPrintWithoutClosingBenchmarkTable(); + void lazyPrintRunInfo(); + void lazyPrintGroupInfo(); + void printTestCaseAndSectionHeader(); - void printClosedHeader(std::string const& _name); - void printOpenHeader(std::string const& _name); + void printClosedHeader(std::string const &_name); + void printOpenHeader(std::string const &_name); - // if string has a : in first line will set indent to follow it on - // subsequent lines - void printHeaderString(std::string const& _string, std::size_t indent = 0); + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString(std::string const &_string, std::size_t indent = 0); - void printTotals(Totals const& totals); - void printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row); + void printTotals(Totals const &totals); + void printSummaryRow(std::string const &label, std::vector const &cols, std::size_t row); - void printTotalsDivider(Totals const& totals); - void printSummaryDivider(); + void printTotalsDivider(Totals const &totals); + void printSummaryDivider(); + void printTestFilters(); - private: - bool m_headerPrinted = false; - }; + private: + bool m_headerPrinted = false; + }; } // end namespace Catch @@ -4230,138 +6510,147 @@ namespace Catch { #include namespace Catch { + enum class XmlFormatting { + None = 0x00, + Indent = 0x01, + Newline = 0x02, + }; - class XmlEncode { - public: - enum ForWhat { ForTextNodes, ForAttributes }; + XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs); + XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs); - XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ); - - void encodeTo( std::ostream& os ) const; - - friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ); - - private: - std::string m_str; - ForWhat m_forWhat; + class XmlEncode { + public: + enum ForWhat { + ForTextNodes, + ForAttributes }; - class XmlWriter { + XmlEncode(std::string const &str, ForWhat forWhat = ForTextNodes); + + void encodeTo(std::ostream &os) const; + + friend std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode); + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + class ScopedElement { public: + ScopedElement(XmlWriter *writer, XmlFormatting fmt); - class ScopedElement { - public: - ScopedElement( XmlWriter* writer ); + ScopedElement(ScopedElement &&other) noexcept; + ScopedElement &operator=(ScopedElement &&other) noexcept; - ScopedElement( ScopedElement&& other ) noexcept; - ScopedElement& operator=( ScopedElement&& other ) noexcept; + ~ScopedElement(); - ~ScopedElement(); + ScopedElement &writeText(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); - ScopedElement& writeText( std::string const& text, bool indent = true ); - - template - ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { - m_writer->writeAttribute( name, attribute ); - return *this; - } - - private: - mutable XmlWriter* m_writer = nullptr; - }; - - XmlWriter( std::ostream& os = Catch::cout() ); - ~XmlWriter(); - - XmlWriter( XmlWriter const& ) = delete; - XmlWriter& operator=( XmlWriter const& ) = delete; - - XmlWriter& startElement( std::string const& name ); - - ScopedElement scopedElement( std::string const& name ); - - XmlWriter& endElement(); - - XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ); - - XmlWriter& writeAttribute( std::string const& name, bool attribute ); - - template - XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { - ReusableStringStream rss; - rss << attribute; - return writeAttribute( name, rss.str() ); - } - - XmlWriter& writeText( std::string const& text, bool indent = true ); - - XmlWriter& writeComment( std::string const& text ); - - void writeStylesheetRef( std::string const& url ); - - XmlWriter& writeBlankLine(); - - void ensureTagClosed(); + template + ScopedElement &writeAttribute(std::string const &name, T const &attribute) { + m_writer->writeAttribute(name, attribute); + return *this; + } private: - - void writeDeclaration(); - - void newlineIfNecessary(); - - bool m_tagIsOpen = false; - bool m_needsNewline = false; - std::vector m_tags; - std::string m_indent; - std::ostream& m_os; + mutable XmlWriter *m_writer = nullptr; + XmlFormatting m_fmt; }; -} + XmlWriter(std::ostream &os = Catch::cout()); + ~XmlWriter(); + + XmlWriter(XmlWriter const &) = delete; + XmlWriter &operator=(XmlWriter const &) = delete; + + XmlWriter &startElement(std::string const &name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + ScopedElement scopedElement(std::string const &name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter &endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter &writeAttribute(std::string const &name, std::string const &attribute); + + XmlWriter &writeAttribute(std::string const &name, bool attribute); + + template + XmlWriter &writeAttribute(std::string const &name, T const &attribute) { + ReusableStringStream rss; + rss << attribute; + return writeAttribute(name, rss.str()); + } + + XmlWriter &writeText(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + XmlWriter &writeComment(std::string const &text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent); + + void writeStylesheetRef(std::string const &url); + + XmlWriter &writeBlankLine(); + + void ensureTagClosed(); + + private: + void applyFormatting(XmlFormatting fmt); + + void writeDeclaration(); + + void newlineIfNecessary(); + + bool m_tagIsOpen = false; + bool m_needsNewline = false; + std::vector m_tags; + std::string m_indent; + std::ostream &m_os; + }; + +} // namespace Catch // end catch_xmlwriter.h namespace Catch { + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter(ReporterConfig const &_config); - class JunitReporter : public CumulativeReporterBase { - public: - JunitReporter(ReporterConfig const& _config); + ~JunitReporter() override; - ~JunitReporter() override; + static std::string getDescription(); - static std::string getDescription(); + void noMatchingTestCases(std::string const & /*spec*/) override; - void noMatchingTestCases(std::string const& /*spec*/) override; + void testRunStarting(TestRunInfo const &runInfo) override; - void testRunStarting(TestRunInfo const& runInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; - void testGroupStarting(GroupInfo const& groupInfo) override; + void testCaseStarting(TestCaseInfo const &testCaseInfo) override; + bool assertionEnded(AssertionStats const &assertionStats) override; - void testCaseStarting(TestCaseInfo const& testCaseInfo) override; - bool assertionEnded(AssertionStats const& assertionStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; - void testCaseEnded(TestCaseStats const& testCaseStats) override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; - void testGroupEnded(TestGroupStats const& testGroupStats) override; + void testRunEndedCumulative() override; - void testRunEndedCumulative() override; + void writeGroup(TestGroupNode const &groupNode, double suiteTime); - void writeGroup(TestGroupNode const& groupNode, double suiteTime); + void writeTestCase(TestCaseNode const &testCaseNode); - void writeTestCase(TestCaseNode const& testCaseNode); + void writeSection(std::string const &className, std::string const &rootName, SectionNode const §ionNode); - void writeSection(std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode); + void writeAssertions(SectionNode const §ionNode); + void writeAssertion(AssertionStats const &stats); - void writeAssertions(SectionNode const& sectionNode); - void writeAssertion(AssertionStats const& stats); - - XmlWriter xml; - Timer suiteTimer; - std::string stdOutForSuite; - std::string stdErrForSuite; - unsigned int unexpectedExceptions = 0; - bool m_okToFail = false; - }; + XmlWriter xml; + Timer suiteTimer; + std::string stdOutForSuite; + std::string stdErrForSuite; + unsigned int unexpectedExceptions = 0; + bool m_okToFail = false; + }; } // end namespace Catch @@ -4369,47 +6658,53 @@ namespace Catch { // start catch_reporter_xml.h namespace Catch { - class XmlReporter : public StreamingReporterBase { - public: - XmlReporter(ReporterConfig const& _config); + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter(ReporterConfig const &_config); - ~XmlReporter() override; + ~XmlReporter() override; - static std::string getDescription(); + static std::string getDescription(); - virtual std::string getStylesheetRef() const; + virtual std::string getStylesheetRef() const; - void writeSourceInfo(SourceLineInfo const& sourceInfo); + void writeSourceInfo(SourceLineInfo const &sourceInfo); - public: // StreamingReporterBase + public: // StreamingReporterBase + void noMatchingTestCases(std::string const &s) override; - void noMatchingTestCases(std::string const& s) override; + void testRunStarting(TestRunInfo const &testInfo) override; - void testRunStarting(TestRunInfo const& testInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; - void testGroupStarting(GroupInfo const& groupInfo) override; + void testCaseStarting(TestCaseInfo const &testInfo) override; - void testCaseStarting(TestCaseInfo const& testInfo) override; + void sectionStarting(SectionInfo const §ionInfo) override; - void sectionStarting(SectionInfo const& sectionInfo) override; + void assertionStarting(AssertionInfo const &) override; - void assertionStarting(AssertionInfo const&) override; + bool assertionEnded(AssertionStats const &assertionStats) override; - bool assertionEnded(AssertionStats const& assertionStats) override; + void sectionEnded(SectionStats const §ionStats) override; - void sectionEnded(SectionStats const& sectionStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; - void testCaseEnded(TestCaseStats const& testCaseStats) override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; - void testGroupEnded(TestGroupStats const& testGroupStats) override; + void testRunEnded(TestRunStats const &testRunStats) override; - void testRunEnded(TestRunStats const& testRunStats) override; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &) override; + void benchmarkEnded(BenchmarkStats<> const &) override; + void benchmarkFailed(std::string const &) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - private: - Timer m_testCaseTimer; - XmlWriter m_xml; - int m_sectionDepth = 0; - }; + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth = 0; + }; } // end namespace Catch @@ -4418,6 +6713,1040 @@ namespace Catch { // end catch_external_interfaces.h #endif +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +// start catch_benchmarking_all.hpp + +// A proxy header that includes all of the benchmarking headers to allow +// concise include of the benchmarking features. You should prefer the +// individual includes in standard use. + +// start catch_benchmark.hpp + +// Benchmark + +// start catch_chronometer.hpp + +// User-facing chronometer + +// start catch_clock.hpp + +// Clocks + +#include +#include + +namespace Catch { + namespace Benchmark { + template + using ClockDuration = typename Clock::duration; + template + using FloatDuration = std::chrono::duration; + + template + using TimePoint = typename Clock::time_point; + + using default_clock = std::chrono::steady_clock; + + template + struct now { + TimePoint operator()() const { return Clock::now(); } + }; + + using fp_seconds = std::chrono::duration>; + } // namespace Benchmark +} // namespace Catch + +// end catch_clock.hpp +// start catch_optimizer.hpp + +// Hinting the optimizer + +#if defined(_MSC_VER) +#include // atomic_thread_fence +#endif + +namespace Catch { + namespace Benchmark { +#if defined(__GNUC__) || defined(__clang__) + template + inline void keep_memory(T *p) { + asm volatile("" + : + : "g"(p) + : "memory"); + } + inline void keep_memory() { + asm volatile("" + : + : + : "memory"); + } + + namespace Detail { + inline void optimizer_barrier() { + keep_memory(); + } + } // namespace Detail +#elif defined(_MSC_VER) + +#pragma optimize("", off) + template + inline void keep_memory(T *p) { + // thanks @milleniumbug + *reinterpret_cast(p) = *reinterpret_cast(p); + } +// TODO equivalent keep_memory() +#pragma optimize("", on) + + namespace Detail { + inline void optimizer_barrier() { + std::atomic_thread_fence(std::memory_order_seq_cst); + } + } // namespace Detail + +#endif + + template + inline void deoptimize_value(T &&x) { + keep_memory(&x); + } + + template + inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> typename std::enable_if::value>::type { + deoptimize_value(std::forward(fn)(std::forward(args...))); + } + + template + inline auto invoke_deoptimized(Fn &&fn, Args &&...args) -> typename std::enable_if::value>::type { + std::forward(fn)(std::forward(args...)); + } + } // namespace Benchmark +} // namespace Catch + +// end catch_optimizer.hpp +// start catch_complete_invoke.hpp + +// Invoke with a special case for void + +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct CompleteType { + using type = T; + }; + template<> + struct CompleteType { + struct type { + }; + }; + + template + using CompleteType_t = typename CompleteType::type; + + template + struct CompleteInvoker { + template + static Result invoke(Fun &&fun, Args &&...args) { + return std::forward(fun)(std::forward(args)...); + } + }; + template<> + struct CompleteInvoker { + template + static CompleteType_t invoke(Fun &&fun, Args &&...args) { + std::forward(fun)(std::forward(args)...); + return {}; + } + }; + + // invoke and not return void :( + template + CompleteType_t> complete_invoke(Fun &&fun, Args &&...args) { + return CompleteInvoker>::invoke(std::forward(fun), std::forward(args)...); + } + + const std::string benchmarkErrorMsg = "a benchmark failed to run successfully"; + } // namespace Detail + + template + Detail::CompleteType_t> user_code(Fun &&fun) { + CATCH_TRY { return Detail::complete_invoke(std::forward(fun)); } + CATCH_CATCH_ALL { + getResultCapture().benchmarkFailed(translateActiveException()); + CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg); + } + } + } // namespace Benchmark +} // namespace Catch + +// end catch_complete_invoke.hpp +namespace Catch { + namespace Benchmark { + namespace Detail { + struct ChronometerConcept { + virtual void start() = 0; + virtual void finish() = 0; + virtual ~ChronometerConcept() = default; + }; + template + struct ChronometerModel final : public ChronometerConcept { + void start() override { started = Clock::now(); } + void finish() override { finished = Clock::now(); } + + ClockDuration elapsed() const { return finished - started; } + + TimePoint started; + TimePoint finished; + }; + } // namespace Detail + + struct Chronometer { + public: + template + void measure(Fun &&fun) { + measure(std::forward(fun), is_callable()); + } + + int runs() const { return k; } + + Chronometer(Detail::ChronometerConcept &meter, int k) + : impl(&meter) + , k(k) { + } + + private: + template + void measure(Fun &&fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); + } + + template + void measure(Fun &&fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < k; ++i) + invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } + + Detail::ChronometerConcept *impl; + int k; + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_chronometer.hpp +// start catch_environment.hpp + +// Environment information + +namespace Catch { + namespace Benchmark { + template + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template + operator EnvironmentEstimate() const { + return {mean, outliers}; + } + }; + template + struct Environment { + using clock_type = Clock; + EnvironmentEstimate> clock_resolution; + EnvironmentEstimate> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_environment.hpp +// start catch_execution_plan.hpp + +// Execution plan + +// start catch_benchmark_function.hpp + +// Dumb std::function implementation for consistent call overhead + +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + using Decay = typename std::decay::type; + template + struct is_related : std::is_same, Decay> { + }; + + /// We need to reinvent std::function because every piece of code that might add overhead + /// in a measurement context needs to have consistent performance characteristics so that we + /// can account for it in the measurement. + /// Implementations of std::function with optimizations that aren't always applicable, like + /// small buffer optimizations, are not uncommon. + /// This is effectively an implementation of std::function without any such optimizations; + /// it may be slow, but it is consistently slow. + struct BenchmarkFunction { + private: + struct callable { + virtual void call(Chronometer meter) const = 0; + virtual callable *clone() const = 0; + virtual ~callable() = default; + }; + template + struct model : public callable { + model(Fun &&fun) + : fun(std::move(fun)) { + } + model(Fun const &fun) + : fun(fun) { + } + + model *clone() const override { return new model(*this); } + + void call(Chronometer meter) const override { call(meter, is_callable()); } + void call(Chronometer meter, std::true_type) const { fun(meter); } + void call(Chronometer meter, std::false_type) const { meter.measure(fun); } + + Fun fun; + }; + + struct do_nothing { + void operator()() const {} + }; + + template + BenchmarkFunction(model *c) + : f(c) { + } + + public: + BenchmarkFunction() + : f(new model{{}}) { + } + + template::value, int>::type = 0> + BenchmarkFunction(Fun &&fun) + : f(new model::type>(std::forward(fun))) { + } + + BenchmarkFunction(BenchmarkFunction &&that) + : f(std::move(that.f)) { + } + + BenchmarkFunction(BenchmarkFunction const &that) + : f(that.f->clone()) { + } + + BenchmarkFunction &operator=(BenchmarkFunction &&that) { + f = std::move(that.f); + return *this; + } + + BenchmarkFunction &operator=(BenchmarkFunction const &that) { + f.reset(that.f->clone()); + return *this; + } + + void operator()(Chronometer meter) const { f->call(meter); } + + private: + std::unique_ptr f; + }; + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_benchmark_function.hpp +// start catch_repeat.hpp + +// repeat algorithm + +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct repeater { + void operator()(int k) const { + for (int i = 0; i < k; ++i) { + fun(); + } + } + Fun fun; + }; + template + repeater::type> repeat(Fun &&fun) { + return {std::forward(fun)}; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_repeat.hpp +// start catch_run_for_at_least.hpp + +// Run a function for a minimum amount of time + +// start catch_measure.hpp + +// Measure + +// start catch_timing.hpp + +// Timing + +#include +#include + +namespace Catch { + namespace Benchmark { + template + struct Timing { + Duration elapsed; + Result result; + int iterations; + }; + template + using TimingOf = Timing, Detail::CompleteType_t>>; + } // namespace Benchmark +} // namespace Catch + +// end catch_timing.hpp +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + TimingOf measure(Fun &&fun, Args &&...args) { + auto start = Clock::now(); + auto &&r = Detail::complete_invoke(fun, std::forward(args)...); + auto end = Clock::now(); + auto delta = end - start; + return {delta, std::forward(r), 1}; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_measure.hpp +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + TimingOf measure_one(Fun &&fun, int iters, std::false_type) { + return Detail::measure(fun, iters); + } + template + TimingOf measure_one(Fun &&fun, int iters, std::true_type) { + Detail::ChronometerModel meter; + auto &&result = Detail::complete_invoke(fun, Chronometer(meter, iters)); + + return {meter.elapsed(), std::move(result), iters}; + } + + template + using run_for_at_least_argument_t = typename std::conditional::value, Chronometer, int>::type; + + struct optimized_away_error : std::exception { + const char *what() const noexcept override { return "could not measure benchmark, maybe it was optimized away"; } + }; + + template + TimingOf> run_for_at_least(ClockDuration how_long, int seed, Fun &&fun) { + auto iters = seed; + while (iters < (1 << 30)) { + auto &&Timing = measure_one(fun, iters, is_callable()); + + if (Timing.elapsed >= how_long) { + return {Timing.elapsed, std::move(Timing.result), iters}; + } + iters *= 2; + } + throw optimized_away_error{}; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_run_for_at_least.hpp +#include + +namespace Catch { + namespace Benchmark { + template + struct ExecutionPlan { + int iterations_per_sample; + Duration estimated_duration; + Detail::BenchmarkFunction benchmark; + Duration warmup_time; + int warmup_iterations; + + template + operator ExecutionPlan() const { + return {iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations}; + } + + template + std::vector> run(const IConfig &cfg, Environment> env) const { + // warmup a bit + Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), + warmup_iterations, + Detail::repeat(now{})); + + std::vector> times; + times.reserve(cfg.benchmarkSamples()); + std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + Detail::ChronometerModel model; + this->benchmark(Chronometer(model, iterations_per_sample)); + auto sample_time = model.elapsed() - env.clock_cost.mean; + if (sample_time < FloatDuration::zero()) + sample_time = FloatDuration::zero(); + return sample_time / iterations_per_sample; + }); + return times; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_execution_plan.hpp +// start catch_estimate_clock.hpp + +// Environment measurement + +// start catch_stats.hpp + +// Statistical analysis tools + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + using sample = std::vector; + + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); + + template + OutlierClassification classify_outliers(Iterator first, Iterator last) { + std::vector copy(first, last); + + auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); + auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); + auto iqr = q3 - q1; + auto los = q1 - (iqr * 3.); + auto lom = q1 - (iqr * 1.5); + auto him = q3 + (iqr * 1.5); + auto his = q3 + (iqr * 3.); + + OutlierClassification o; + for (; first != last; ++first) { + auto &&t = *first; + if (t < los) + ++o.low_severe; + else if (t < lom) + ++o.low_mild; + else if (t > his) + ++o.high_severe; + else if (t > him) + ++o.high_mild; + ++o.samples_seen; + } + return o; + } + + template + double mean(Iterator first, Iterator last) { + auto count = last - first; + double sum = std::accumulate(first, last, 0.); + return sum / count; + } + + template + sample resample(URng &rng, int resamples, Iterator first, Iterator last, Estimator &estimator) { + auto n = last - first; + std::uniform_int_distribution dist(0, n - 1); + + sample out; + out.reserve(resamples); + std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { + std::vector resampled; + resampled.reserve(n); + std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[dist(rng)]; }); + return estimator(resampled.begin(), resampled.end()); + }); + std::sort(out.begin(), out.end()); + return out; + } + + template + sample jackknife(Estimator &&estimator, Iterator first, Iterator last) { + auto n = last - first; + auto second = std::next(first); + sample results; + results.reserve(n); + + for (auto it = first; it != last; ++it) { + std::iter_swap(it, first); + results.push_back(estimator(second, last)); + } + + return results; + } + + inline double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } + + double erfc_inv(double x); + + double normal_quantile(double p); + + template + Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const &resample, Estimator &&estimator) { + auto n_samples = last - first; + + double point = estimator(first, last); + // Degenerate case with a single sample + if (n_samples == 1) + return {point, point, point, confidence_level}; + + sample jack = jackknife(estimator, first, last); + double jack_mean = mean(jack.begin(), jack.end()); + double sum_squares, sum_cubes; + std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), + jack.end(), + std::make_pair(0., 0.), + [jack_mean](std::pair sqcb, double x) -> std::pair { + auto d = jack_mean - x; + auto d2 = d * d; + auto d3 = d2 * d; + return {sqcb.first + d2, sqcb.second + d3}; + }); + + double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); + int n = static_cast(resample.size()); + double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / (double)n; + // degenerate case with uniform samples + if (prob_n == 0) + return {point, point, point, confidence_level}; + + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); + + auto cumn = [n](double x) -> int { return std::lround(normal_cdf(x) * n); }; + auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; + double b1 = bias + z1; + double b2 = bias - z1; + double a1 = a(b1); + double a2 = a(b2); + auto lo = (std::max)(cumn(a1), 0); + auto hi = (std::min)(cumn(a2), n - 1); + + return {point, resample[lo], resample[hi], confidence_level}; + } + + double outlier_variance(Estimate mean, Estimate stddev, int n); + + struct bootstrap_analysis { + Estimate mean; + Estimate standard_deviation; + double outlier_variance; + }; + + bootstrap_analysis + analyse_samples(double confidence_level, int n_resamples, std::vector::iterator first, std::vector::iterator last); + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_stats.hpp +#include +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + std::vector resolution(int k) { + std::vector> times; + times.reserve(k + 1); + std::generate_n(std::back_inserter(times), k + 1, now{}); + + std::vector deltas; + deltas.reserve(k); + std::transform(std::next(times.begin()), times.end(), times.begin(), std::back_inserter(deltas), [](TimePoint a, TimePoint b) { + return static_cast((a - b).count()); + }); + + return deltas; + } + + const auto warmup_iterations = 10000; + const auto warmup_time = std::chrono::milliseconds(100); + const auto minimum_ticks = 1000; + const auto warmup_seed = 10000; + const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + const auto clock_cost_estimation_tick_limit = 100000; + const auto clock_cost_estimation_time = std::chrono::milliseconds(10); + const auto clock_cost_estimation_iterations = 10000; + + template + int warmup() { + return run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_seed, &resolution).iterations; + } + template + EnvironmentEstimate> estimate_clock_resolution(int iterations) { + auto r = run_for_at_least(std::chrono::duration_cast>(clock_resolution_estimation_time), + iterations, + &resolution) + .result; + return { + FloatDuration(mean(r.begin(), r.end())), + classify_outliers(r.begin(), r.end()), + }; + } + template + EnvironmentEstimate> estimate_clock_cost(FloatDuration resolution) { + auto time_limit = (std::min)(resolution * clock_cost_estimation_tick_limit, FloatDuration(clock_cost_estimation_time_limit)); + auto time_clock = [](int k) { + return Detail::measure([k] { + for (int i = 0; i < k; ++i) { + volatile auto ignored = Clock::now(); + (void)ignored; + } + }) + .elapsed; + }; + time_clock(1); + int iters = clock_cost_estimation_iterations; + auto &&r = run_for_at_least(std::chrono::duration_cast>(clock_cost_estimation_time), iters, time_clock); + std::vector times; + int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); + times.reserve(nsamples); + std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { + return static_cast((time_clock(r.iterations) / r.iterations).count()); + }); + return { + FloatDuration(mean(times.begin(), times.end())), + classify_outliers(times.begin(), times.end()), + }; + } + + template + Environment> measure_environment() { + static Environment> *env = nullptr; + if (env) { + return *env; + } + + auto iters = Detail::warmup(); + auto resolution = Detail::estimate_clock_resolution(iters); + auto cost = Detail::estimate_clock_cost(resolution.mean); + + env = new Environment>{resolution, cost}; + return *env; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_estimate_clock.hpp +// start catch_analyse.hpp + +// Run and analyse one benchmark + +// start catch_sample_analysis.hpp + +// Benchmark results + +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + template + struct SampleAnalysis { + std::vector samples; + Estimate mean; + Estimate standard_deviation; + OutlierClassification outliers; + double outlier_variance; + + template + operator SampleAnalysis() const { + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + return { + std::move(samples2), + mean, + standard_deviation, + outliers, + outlier_variance, + }; + } + }; + } // namespace Benchmark +} // namespace Catch + +// end catch_sample_analysis.hpp +#include +#include +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + SampleAnalysis analyse(const IConfig &cfg, Environment, Iterator first, Iterator last) { + if (!cfg.benchmarkNoAnalysis()) { + std::vector samples; + samples.reserve(last - first); + std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + + auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), + cfg.benchmarkResamples(), + samples.begin(), + samples.end()); + auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); + + auto wrap_estimate = [](Estimate e) { + return Estimate{ + Duration(e.point), + Duration(e.lower_bound), + Duration(e.upper_bound), + e.confidence_interval, + }; + }; + std::vector samples2; + samples2.reserve(samples.size()); + std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + return { + std::move(samples2), + wrap_estimate(analysis.mean), + wrap_estimate(analysis.standard_deviation), + outliers, + analysis.outlier_variance, + }; + } else { + std::vector samples; + samples.reserve(last - first); + + Duration mean = Duration(0); + int i = 0; + for (auto it = first; it < last; ++it, ++i) { + samples.push_back(Duration(*it)); + mean += Duration(*it); + } + mean /= i; + + return {std::move(samples), + Estimate{mean, mean, mean, 0.0}, + Estimate{Duration(0), Duration(0), Duration(0), 0.0}, + OutlierClassification{}, + 0.0}; + } + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +// end catch_analyse.hpp +#include +#include +#include +#include +#include + +namespace Catch { + namespace Benchmark { + struct Benchmark { + Benchmark(std::string &&name) + : name(std::move(name)) { + } + + template + Benchmark(std::string &&name, FUN &&func) + : fun(std::move(func)) + , name(std::move(name)) { + } + + template + ExecutionPlan> prepare(const IConfig &cfg, Environment> env) const { + auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; + auto run_time = std::max(min_time, std::chrono::duration_cast(cfg.benchmarkWarmupTime())); + auto &&test = Detail::run_for_at_least(std::chrono::duration_cast>(run_time), 1, fun); + int new_iters = static_cast(std::ceil(min_time * test.iterations / test.elapsed)); + return {new_iters, + test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), + fun, + std::chrono::duration_cast>(cfg.benchmarkWarmupTime()), + Detail::warmup_iterations}; + } + + template + void run() { + IConfigPtr cfg = getCurrentContext().getConfig(); + + auto env = Detail::measure_environment(); + + getResultCapture().benchmarkPreparing(name); + CATCH_TRY { + auto plan = user_code([&] { return prepare(*cfg, env); }); + + BenchmarkInfo info{name, + plan.estimated_duration.count(), + plan.iterations_per_sample, + cfg->benchmarkSamples(), + cfg->benchmarkResamples(), + env.clock_resolution.mean.count(), + env.clock_cost.mean.count()}; + + getResultCapture().benchmarkStarting(info); + + auto samples = user_code([&] { return plan.template run(*cfg, env); }); + + auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); + BenchmarkStats> stats{info, + analysis.samples, + analysis.mean, + analysis.standard_deviation, + analysis.outliers, + analysis.outlier_variance}; + getResultCapture().benchmarkEnded(stats); + } + CATCH_CATCH_ALL { + if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow. + std::rethrow_exception(std::current_exception()); + } + } + + // sets lambda to be used in fun *and* executes benchmark! + template::value, int>::type = 0> + Benchmark &operator=(Fun func) { + fun = Detail::BenchmarkFunction(func); + run(); + return *this; + } + + explicit operator bool() { return true; } + + private: + Detail::BenchmarkFunction fun; + std::string name; + }; + } // namespace Benchmark +} // namespace Catch + +#define INTERNAL_CATCH_GET_1_ARG(arg1, arg2, ...) arg1 +#define INTERNAL_CATCH_GET_2_ARG(arg1, arg2, ...) arg2 + +#define INTERNAL_CATCH_BENCHMARK(BenchmarkName, name, benchmarkIndex) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&](int benchmarkIndex) + +#define INTERNAL_CATCH_BENCHMARK_ADVANCED(BenchmarkName, name) \ + if (Catch::Benchmark::Benchmark BenchmarkName{name}) \ + BenchmarkName = [&] + +// end catch_benchmark.hpp +// start catch_constructor.hpp + +// Constructor and destructor helpers + +#include + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct ObjectStorage { + using TStorage = typename std::aligned_storage::value>::type; + + ObjectStorage() + : data() { + } + + ObjectStorage(const ObjectStorage &other) { new (&data) T(other.stored_object()); } + + ObjectStorage(ObjectStorage &&other) { new (&data) T(std::move(other.stored_object())); } + + ~ObjectStorage() { destruct_on_exit(); } + + template + void construct(Args &&...args) { + new (&data) T(std::forward(args)...); + } + + template + typename std::enable_if::type destruct() { + stored_object().~T(); + } + + private: + // If this is a constructor benchmark, destruct the underlying object + template + void destruct_on_exit(typename std::enable_if::type * = 0) { + destruct(); + } + // Otherwise, don't + template + void destruct_on_exit(typename std::enable_if::type * = 0) { + } + + T &stored_object() { return *static_cast(static_cast(&data)); } + + T const &stored_object() const { return *static_cast(static_cast(&data)); } + + TStorage data; + }; + } // namespace Detail + + template + using storage_for = Detail::ObjectStorage; + + template + using destructable_object = Detail::ObjectStorage; + } // namespace Benchmark +} // namespace Catch + +// end catch_constructor.hpp +// end catch_benchmarking_all.hpp +#endif + #endif // ! CATCH_CONFIG_IMPL_ONLY #ifdef CATCH_IMPL @@ -4431,171 +7760,160 @@ namespace Catch { // Keep these here for external reporters // start catch_test_case_tracker.h +#include #include #include -#include namespace Catch { -namespace TestCaseTracking { - + namespace TestCaseTracking { struct NameAndLocation { - std::string name; - SourceLineInfo location; + std::string name; + SourceLineInfo location; - NameAndLocation( std::string const& _name, SourceLineInfo const& _location ); + NameAndLocation(std::string const &_name, SourceLineInfo const &_location); + friend bool operator==(NameAndLocation const &lhs, NameAndLocation const &rhs) { + return lhs.name == rhs.name && lhs.location == rhs.location; + } }; - struct ITracker; + class ITracker; using ITrackerPtr = std::shared_ptr; - struct ITracker { - virtual ~ITracker(); + class ITracker { + NameAndLocation m_nameAndLocation; - // static queries - virtual NameAndLocation const& nameAndLocation() const = 0; + public: + ITracker(NameAndLocation const &nameAndLoc) + : m_nameAndLocation(nameAndLoc) { + } - // dynamic queries - virtual bool isComplete() const = 0; // Successfully completed or failed - virtual bool isSuccessfullyCompleted() const = 0; - virtual bool isOpen() const = 0; // Started but not complete - virtual bool hasChildren() const = 0; + // static queries + NameAndLocation const &nameAndLocation() const { return m_nameAndLocation; } - virtual ITracker& parent() = 0; + virtual ~ITracker(); - // actions - virtual void close() = 0; // Successfully complete - virtual void fail() = 0; - virtual void markAsNeedingAnotherRun() = 0; + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + virtual bool hasStarted() const = 0; - virtual void addChild( ITrackerPtr const& child ) = 0; - virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0; - virtual void openChild() = 0; + virtual ITracker &parent() = 0; - // Debug/ checking - virtual bool isSectionTracker() const = 0; - virtual bool isIndexTracker() const = 0; + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild(ITrackerPtr const &child) = 0; + virtual ITrackerPtr findChild(NameAndLocation const &nameAndLocation) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isGeneratorTracker() const = 0; }; class TrackerContext { + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; - enum RunState { - NotStarted, - Executing, - CompletedCycle - }; - - ITrackerPtr m_rootTracker; - ITracker* m_currentTracker = nullptr; - RunState m_runState = NotStarted; + ITrackerPtr m_rootTracker; + ITracker *m_currentTracker = nullptr; + RunState m_runState = NotStarted; public: + ITracker &startRun(); + void endRun(); - static TrackerContext& instance(); + void startCycle(); + void completeCycle(); - ITracker& startRun(); - void endRun(); - - void startCycle(); - void completeCycle(); - - bool completedCycle() const; - ITracker& currentTracker(); - void setCurrentTracker( ITracker* tracker ); + bool completedCycle() const; + ITracker ¤tTracker(); + void setCurrentTracker(ITracker *tracker); }; class TrackerBase : public ITracker { protected: - enum CycleState { - NotStarted, - Executing, - ExecutingChildren, - NeedsAnotherRun, - CompletedSuccessfully, - Failed - }; + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; - class TrackerHasName { - NameAndLocation m_nameAndLocation; - public: - TrackerHasName( NameAndLocation const& nameAndLocation ); - bool operator ()( ITrackerPtr const& tracker ) const; - }; - - using Children = std::vector; - NameAndLocation m_nameAndLocation; - TrackerContext& m_ctx; - ITracker* m_parent; - Children m_children; - CycleState m_runState = NotStarted; + using Children = std::vector; + TrackerContext &m_ctx; + ITracker *m_parent; + Children m_children; + CycleState m_runState = NotStarted; public: - TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); - NameAndLocation const& nameAndLocation() const override; - bool isComplete() const override; - bool isSuccessfullyCompleted() const override; - bool isOpen() const override; - bool hasChildren() const override; + bool isComplete() const override; + bool isSuccessfullyCompleted() const override; + bool isOpen() const override; + bool hasChildren() const override; + bool hasStarted() const override { return m_runState != NotStarted; } - void addChild( ITrackerPtr const& child ) override; + void addChild(ITrackerPtr const &child) override; - ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override; - ITracker& parent() override; + ITrackerPtr findChild(NameAndLocation const &nameAndLocation) override; + ITracker &parent() override; - void openChild() override; + void openChild() override; - bool isSectionTracker() const override; - bool isIndexTracker() const override; + bool isSectionTracker() const override; + bool isGeneratorTracker() const override; - void open(); + void open(); - void close() override; - void fail() override; - void markAsNeedingAnotherRun() override; + void close() override; + void fail() override; + void markAsNeedingAnotherRun() override; private: - void moveToParent(); - void moveToThis(); + void moveToParent(); + void moveToThis(); }; class SectionTracker : public TrackerBase { - std::vector m_filters; + std::vector m_filters; + std::string m_trimmed_name; + public: - SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ); + SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent); - bool isSectionTracker() const override; + bool isSectionTracker() const override; - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ); + bool isComplete() const override; - void tryOpen(); + static SectionTracker &acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation); - void addInitialFilters( std::vector const& filters ); - void addNextFilters( std::vector const& filters ); + void tryOpen(); + + void addInitialFilters(std::vector const &filters); + void addNextFilters(std::vector const &filters); + //! Returns filters active in this tracker + std::vector const &getFilters() const; + //! Returns whitespace-trimmed name of the tracked section + std::string const &trimmedName() const; }; - class IndexTracker : public TrackerBase { - int m_size; - int m_index = -1; - public: - IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ); + } // namespace TestCaseTracking - bool isIndexTracker() const override; - void close() override; - - static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ); - - int index() const; - - void moveNext(); - }; - -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; + using TestCaseTracking::ITracker; + using TestCaseTracking::SectionTracker; + using TestCaseTracking::TrackerContext; } // namespace Catch @@ -4604,152 +7922,364 @@ using TestCaseTracking::IndexTracker; // start catch_leak_detector.h namespace Catch { + struct LeakDetector { + LeakDetector(); + ~LeakDetector(); + }; - struct LeakDetector { - LeakDetector(); - }; - -} +} // namespace Catch // end catch_leak_detector.h // Cpp files will be included in the single-header file here +// start catch_stats.cpp + +// Statistical analysis tools + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + +#include +#include + +#if defined(CATCH_CONFIG_USE_ASYNC) +#include +#endif + +namespace { + double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log((1.0 - x) * (1.0 + x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + double standard_deviation(std::vector::iterator first, std::vector::iterator last) { + auto m = Catch::Benchmark::Detail::mean(first, last); + double variance = std::accumulate(first, + last, + 0., + [m](double a, double b) { + double diff = b - m; + return a + diff * diff; + }) + / (last - first); + return std::sqrt(variance); + } + +} // namespace + +namespace Catch { + namespace Benchmark { + namespace Detail { + double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last) { + auto count = last - first; + double idx = (count - 1) * k / static_cast(q); + int j = static_cast(idx); + double g = idx - j; + std::nth_element(first, first + j, last); + auto xj = first[j]; + if (g == 0) + return xj; + + auto xj1 = *std::min_element(first + (j + 1), last); + return xj + g * (xj1 - xj); + } + + double erfc_inv(double x) { + return erf_inv(1.0 - x); + } + + double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; + } + + double outlier_variance(Estimate mean, Estimate stddev, int n) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2](double x) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return (int)(-2. * k0 / (k1 + std::sqrt(det))); + }; + + auto var_out = [n, sb2, sg2](double c) { + double nc = n - c; + return (nc / n) * (sb2 - nc * sg2); + }; + + return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; + } + + bootstrap_analysis + analyse_samples(double confidence_level, int n_resamples, std::vector::iterator first, std::vector::iterator last) { + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS + static std::random_device entropy; + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ + + auto mean = &Detail::mean::iterator>; + auto stddev = &standard_deviation; + +#if defined(CATCH_CONFIG_USE_ASYNC) + auto Estimate = [=](double (*f)(std::vector::iterator, std::vector::iterator)) { + auto seed = entropy(); + return std::async(std::launch::async, [=] { + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }); + }; + + auto mean_future = Estimate(mean); + auto stddev_future = Estimate(stddev); + + auto mean_estimate = mean_future.get(); + auto stddev_estimate = stddev_future.get(); +#else + auto Estimate = [=](double (*f)(std::vector::iterator, std::vector::iterator)) { + auto seed = entropy(); + std::mt19937 rng(seed); + auto resampled = resample(rng, n_resamples, first, last, f); + return bootstrap(confidence_level, first, last, resampled, f); + }; + + auto mean_estimate = Estimate(mean); + auto stddev_estimate = Estimate(stddev); +#endif // CATCH_USE_ASYNC + + double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); + + return {mean_estimate, stddev_estimate, outlier_variance}; + } + } // namespace Detail + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING \ + // end catch_stats.cpp // start catch_approx.cpp #include #include namespace { - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool marginComparison(double lhs, double rhs, double margin) { return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} + } -} +} // namespace namespace Catch { -namespace Detail { - - Approx::Approx ( double value ) - : m_epsilon( std::numeric_limits::epsilon()*100 ), - m_margin( 0.0 ), - m_scale( 0.0 ), - m_value( value ) - {} + namespace Detail { + Approx::Approx(double value) + : m_epsilon(std::numeric_limits::epsilon() * 100) + , m_margin(0.0) + , m_scale(0.0) + , m_value(value) { + } Approx Approx::custom() { - return Approx( 0 ); + return Approx(0); + } + + Approx Approx::operator-() const { + auto temp(*this); + temp.m_value = -temp.m_value; + return temp; } std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; - return rss.str(); + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify(m_value) << " )"; + return rss.str(); } bool Approx::equalityComparisonImpl(const double other) const { - // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value - // Thanks to Richard Harris for his help refining the scaled margin value - return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value))); + // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value + // Thanks to Richard Harris for his help refining the scaled margin value + return marginComparison(m_value, other, m_margin) + || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value) ? 0 : m_value))); } -} // end namespace Detail + void Approx::setMargin(double newMargin) { + CATCH_ENFORCE(newMargin >= 0, "Invalid Approx::margin: " << newMargin << '.' << " Approx::Margin has to be non-negative."); + m_margin = newMargin; + } -std::string StringMaker::convert(Catch::Detail::Approx const& value) { + void Approx::setEpsilon(double newEpsilon) { + CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, + "Invalid Approx::epsilon: " << newEpsilon << '.' << " Approx::epsilon has to be in [0, 1]"); + m_epsilon = newEpsilon; + } + + } // end namespace Detail + + namespace literals { + Detail::Approx operator"" _a(long double val) { + return Detail::Approx(val); + } + Detail::Approx operator"" _a(unsigned long long val) { + return Detail::Approx(val); + } + } // end namespace literals + + std::string StringMaker::convert(Catch::Detail::Approx const &value) { return value.toString(); -} + } } // end namespace Catch // end catch_approx.cpp // start catch_assertionhandler.cpp -// start catch_context.h - -#include - -namespace Catch { - - struct IResultCapture; - struct IRunner; - struct IConfig; - struct IMutableContext; - - using IConfigPtr = std::shared_ptr; - - struct IContext - { - virtual ~IContext(); - - virtual IResultCapture* getResultCapture() = 0; - virtual IRunner* getRunner() = 0; - virtual IConfigPtr const& getConfig() const = 0; - }; - - struct IMutableContext : IContext - { - virtual ~IMutableContext(); - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setRunner( IRunner* runner ) = 0; - virtual void setConfig( IConfigPtr const& config ) = 0; - - private: - static IMutableContext *currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); - }; - - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - return *IMutableContext::currentContext; - } - - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); - } - - void cleanUpContext(); -} - -// end catch_context.h // start catch_debugger.h namespace Catch { - bool isDebuggerActive(); + bool isDebuggerActive(); } #ifdef CATCH_PLATFORM_MAC - #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */ - -#elif defined(CATCH_PLATFORM_LINUX) - // If we can use inline assembler, do it because this allows us to break - // directly at the location of the failing check instead of breaking inside - // raise() called from it, i.e. one stack frame below. - #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) - #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */ - #else // Fall back to the generic way. - #include - - #define CATCH_TRAP() raise(SIGTRAP) - #endif -#elif defined(_MSC_VER) - #define CATCH_TRAP() __debugbreak() -#elif defined(__MINGW32__) - extern "C" __declspec(dllimport) void __stdcall DebugBreak(); - #define CATCH_TRAP() DebugBreak() +#if defined(__i386__) || defined(__x86_64__) +#define CATCH_TRAP() __asm__("int $3\n" \ + : \ + :) /* NOLINT */ +#elif defined(__aarch64__) +#define CATCH_TRAP() __asm__(".inst 0xd4200000") #endif +#elif defined(CATCH_PLATFORM_IPHONE) + +// use inline assembler +#if defined(__i386__) || defined(__x86_64__) +#define CATCH_TRAP() __asm__("int $3") +#elif defined(__aarch64__) +#define CATCH_TRAP() __asm__(".inst 0xd4200000") +#elif defined(__arm__) && !defined(__thumb__) +#define CATCH_TRAP() __asm__(".inst 0xe7f001f0") +#elif defined(__arm__) && defined(__thumb__) +#define CATCH_TRAP() __asm__(".inst 0xde01") +#endif + +#elif defined(CATCH_PLATFORM_LINUX) +// If we can use inline assembler, do it because this allows us to break +// directly at the location of the failing check instead of breaking inside +// raise() called from it, i.e. one stack frame below. +#if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) +#define CATCH_TRAP() asm volatile("int $3") /* NOLINT */ +#else // Fall back to the generic way. +#include + +#define CATCH_TRAP() raise(SIGTRAP) +#endif +#elif defined(_MSC_VER) +#define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) +extern "C" __declspec(dllimport) void __stdcall DebugBreak(); +#define CATCH_TRAP() DebugBreak() +#endif + +#ifndef CATCH_BREAK_INTO_DEBUGGER #ifdef CATCH_TRAP - #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#define CATCH_BREAK_INTO_DEBUGGER() \ + [] { \ + if (Catch::isDebuggerActive()) { \ + CATCH_TRAP(); \ + } \ + }() #else - namespace Catch { - inline void doNothing() {} - } - #define CATCH_BREAK_INTO_DEBUGGER() Catch::doNothing() +#define CATCH_BREAK_INTO_DEBUGGER() [] {}() +#endif #endif // end catch_debugger.h @@ -4757,438 +8287,379 @@ namespace Catch { // start catch_fatal_condition.h -// start catch_windows_h_proxy.h - - -#if defined(CATCH_PLATFORM_WINDOWS) - -#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) -# define CATCH_DEFINED_NOMINMAX -# define NOMINMAX -#endif -#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) -# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -#endif - -#ifdef __AFXDLL -#include -#else -#include -#endif - -#ifdef CATCH_DEFINED_NOMINMAX -# undef NOMINMAX -#endif -#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN -# undef WIN32_LEAN_AND_MEAN -#endif - -#endif // defined(CATCH_PLATFORM_WINDOWS) - -// end catch_windows_h_proxy.h -#if defined( CATCH_CONFIG_WINDOWS_SEH ) +#include namespace Catch { + // Wrapper for platform-specific fatal error (signals/SEH) handlers + // + // Tries to be cooperative with other handlers, and not step over + // other handlers. This means that unknown structured exceptions + // are passed on, previous signal handlers are called, and so on. + // + // Can only be instantiated once, and assumes that once a signal + // is caught, the binary will end up terminating. Thus, there + class FatalConditionHandler { + bool m_started = false; - struct FatalConditionHandler { + // Install/disengage implementation for specific platform. + // Should be if-defed to work on current platform, can assume + // engage-disengage 1:1 pairing. + void engage_platform(); + void disengage_platform(); - static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); - FatalConditionHandler(); - static void reset(); - ~FatalConditionHandler(); + public: + // Should also have platform-specific implementations as needed + FatalConditionHandler(); + ~FatalConditionHandler(); - private: - static bool isSet; - static ULONG guaranteeSize; - static PVOID exceptionHandlerHandle; - }; + void engage() { + assert(!m_started && "Handler cannot be installed twice."); + m_started = true; + engage_platform(); + } -} // namespace Catch + void disengage() { + assert(m_started && "Handler cannot be uninstalled without being installed first"); + m_started = false; + disengage_platform(); + } + }; -#elif defined ( CATCH_CONFIG_POSIX_SIGNALS ) + //! Simple RAII guard for (dis)engaging the FatalConditionHandler + class FatalConditionHandlerGuard { + FatalConditionHandler *m_handler; -#include + public: + FatalConditionHandlerGuard(FatalConditionHandler *handler) + : m_handler(handler) { + m_handler->engage(); + } + ~FatalConditionHandlerGuard() { m_handler->disengage(); } + }; -namespace Catch { - - struct FatalConditionHandler { - - static bool isSet; - static struct sigaction oldSigActions[]; - static stack_t oldSigStack; - static char altStackMem[]; - - static void handleSignal( int sig ); - - FatalConditionHandler(); - ~FatalConditionHandler(); - static void reset(); - }; - -} // namespace Catch - -#else - -namespace Catch { - struct FatalConditionHandler { - void reset(); - }; -} - -#endif +} // end namespace Catch // end catch_fatal_condition.h #include namespace Catch { + struct IMutableContext; - struct IMutableContext; + /////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// + class RunContext : public IResultCapture, public IRunner { + public: + RunContext(RunContext const &) = delete; + RunContext &operator=(RunContext const &) = delete; - class RunContext : public IResultCapture, public IRunner { + explicit RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter); - public: - RunContext( RunContext const& ) = delete; - RunContext& operator =( RunContext const& ) = delete; + ~RunContext() override; - explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter ); + void testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount); + void testGroupEnded(std::string const &testSpec, Totals const &totals, std::size_t groupIndex, std::size_t groupsCount); - ~RunContext() override; + Totals runTest(TestCase const &testCase); - void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ); - void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ); + IConfigPtr config() const; + IStreamingReporter &reporter() const; - Totals runTest(TestCase const& testCase); + public: // IResultCapture + // Assertion handlers + void handleExpr(AssertionInfo const &info, ITransientExpression const &expr, AssertionReaction &reaction) override; + void handleMessage(AssertionInfo const &info, ResultWas::OfType resultType, StringRef const &message, AssertionReaction &reaction) override; + void handleUnexpectedExceptionNotThrown(AssertionInfo const &info, AssertionReaction &reaction) override; + void handleUnexpectedInflightException(AssertionInfo const &info, std::string const &message, AssertionReaction &reaction) override; + void handleIncomplete(AssertionInfo const &info) override; + void handleNonExpr(AssertionInfo const &info, ResultWas::OfType resultType, AssertionReaction &reaction) override; - IConfigPtr config() const; - IStreamingReporter& reporter() const; + bool sectionStarted(SectionInfo const §ionInfo, Counts &assertions) override; - public: // IResultCapture + void sectionEnded(SectionEndInfo const &endInfo) override; + void sectionEndedEarly(SectionEndInfo const &endInfo) override; - // Assertion handlers - void handleExpr - ( AssertionInfo const& info, - ITransientExpression const& expr, - AssertionReaction& reaction ) override; - void handleMessage - ( AssertionInfo const& info, - ResultWas::OfType resultType, - StringRef const& message, - AssertionReaction& reaction ) override; - void handleUnexpectedExceptionNotThrown - ( AssertionInfo const& info, - AssertionReaction& reaction ) override; - void handleUnexpectedInflightException - ( AssertionInfo const& info, - std::string const& message, - AssertionReaction& reaction ) override; - void handleIncomplete - ( AssertionInfo const& info ) override; - void handleNonExpr - ( AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction ) override; + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & override; - bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &info) override; + void benchmarkEnded(BenchmarkStats<> const &stats) override; + void benchmarkFailed(std::string const &error) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void sectionEnded( SectionEndInfo const& endInfo ) override; - void sectionEndedEarly( SectionEndInfo const& endInfo ) override; + void pushScopedMessage(MessageInfo const &message) override; + void popScopedMessage(MessageInfo const &message) override; - void benchmarkStarting( BenchmarkInfo const& info ) override; - void benchmarkEnded( BenchmarkStats const& stats ) override; + void emplaceUnscopedMessage(MessageBuilder const &builder) override; - void pushScopedMessage( MessageInfo const& message ) override; - void popScopedMessage( MessageInfo const& message ) override; + std::string getCurrentTestName() const override; - std::string getCurrentTestName() const override; + const AssertionResult *getLastResult() const override; - const AssertionResult* getLastResult() const override; + void exceptionEarlyReported() override; - void exceptionEarlyReported() override; + void handleFatalErrorCondition(StringRef message) override; - void handleFatalErrorCondition( StringRef message ) override; + bool lastAssertionPassed() override; - bool lastAssertionPassed() override; + void assertionPassed() override; - void assertionPassed() override; + public: + // !TBD We need to do this another way! + bool aborting() const final; - public: - // !TBD We need to do this another way! - bool aborting() const final; + private: + void runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr); + void invokeActiveTestCase(); - private: + void resetAssertionInfo(); + bool testForMissingAssertions(Counts &assertions); - void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ); - void invokeActiveTestCase(); + void assertionEnded(AssertionResult const &result); + void reportExpr(AssertionInfo const &info, ResultWas::OfType resultType, ITransientExpression const *expr, bool negated); - void resetAssertionInfo(); - bool testForMissingAssertions( Counts& assertions ); + void populateReaction(AssertionReaction &reaction); - void assertionEnded( AssertionResult const& result ); - void reportExpr - ( AssertionInfo const &info, - ResultWas::OfType resultType, - ITransientExpression const *expr, - bool negated ); + private: + void handleUnfinishedSections(); - void populateReaction( AssertionReaction& reaction ); + TestRunInfo m_runInfo; + IMutableContext &m_context; + TestCase const *m_activeTestCase = nullptr; + ITracker *m_testCaseTracker = nullptr; + Option m_lastResult; - private: - - void handleUnfinishedSections(); - - TestRunInfo m_runInfo; - IMutableContext& m_context; - TestCase const* m_activeTestCase = nullptr; - ITracker* m_testCaseTracker; - Option m_lastResult; - - IConfigPtr m_config; - Totals m_totals; - IStreamingReporterPtr m_reporter; - std::vector m_messages; - AssertionInfo m_lastAssertionInfo; - std::vector m_unfinishedSections; - std::vector m_activeSections; - TrackerContext m_trackerContext; - bool m_lastAssertionPassed = false; - bool m_shouldReportUnexpected = true; - bool m_includeSuccessfulResults; - }; + IConfigPtr m_config; + Totals m_totals; + IStreamingReporterPtr m_reporter; + std::vector m_messages; + std::vector m_messageScopes; /* Keeps owners of so-called unscoped messages. */ + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + FatalConditionHandler m_fatalConditionhandler; + bool m_lastAssertionPassed = false; + bool m_shouldReportUnexpected = true; + bool m_includeSuccessfulResults; + }; + void seedRng(IConfig const &config); + unsigned int rngSeed(); } // end namespace Catch // end catch_run_context.h namespace Catch { - - auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& { - expr.streamReconstructedExpression( os ); - return os; + namespace { + auto operator<<(std::ostream &os, ITransientExpression const &expr) -> std::ostream & { + expr.streamReconstructedExpression(os); + return os; } + } // namespace - LazyExpression::LazyExpression( bool isNegated ) - : m_isNegated( isNegated ) - {} + LazyExpression::LazyExpression(bool isNegated) + : m_isNegated(isNegated) { + } - LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {} + LazyExpression::LazyExpression(LazyExpression const &other) + : m_isNegated(other.m_isNegated) { + } - LazyExpression::operator bool() const { - return m_transientExpression != nullptr; + LazyExpression::operator bool() const { + return m_transientExpression != nullptr; + } + + auto operator<<(std::ostream &os, LazyExpression const &lazyExpr) -> std::ostream & { + if (lazyExpr.m_isNegated) + os << "!"; + + if (lazyExpr) { + if (lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression()) + os << "(" << *lazyExpr.m_transientExpression << ")"; + else + os << *lazyExpr.m_transientExpression; + } else { + os << "{** error - unchecked empty expression requested **}"; } + return os; + } - auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& { - if( lazyExpr.m_isNegated ) - os << "!"; + AssertionHandler::AssertionHandler(StringRef const ¯oName, + SourceLineInfo const &lineInfo, + StringRef capturedExpression, + ResultDisposition::Flags resultDisposition) + : m_assertionInfo{macroName, lineInfo, capturedExpression, resultDisposition} + , m_resultCapture(getResultCapture()) { + } - if( lazyExpr ) { - if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() ) - os << "(" << *lazyExpr.m_transientExpression << ")"; - else - os << *lazyExpr.m_transientExpression; - } - else { - os << "{** error - unchecked empty expression requested **}"; - } - return os; + void AssertionHandler::handleExpr(ITransientExpression const &expr) { + m_resultCapture.handleExpr(m_assertionInfo, expr, m_reaction); + } + void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const &message) { + m_resultCapture.handleMessage(m_assertionInfo, resultType, message, m_reaction); + } + + auto AssertionHandler::allowThrows() const -> bool { + return getCurrentContext().getConfig()->allowThrows(); + } + + void AssertionHandler::complete() { + setCompleted(); + if (m_reaction.shouldDebugBreak) { + // If you find your debugger stopping you here then go one level up on the + // call-stack for the code that caused it (typically a failed assertion) + + // (To go back to the test and change execution, jump over the throw, next) + CATCH_BREAK_INTO_DEBUGGER(); } - - AssertionHandler::AssertionHandler - ( StringRef macroName, - SourceLineInfo const& lineInfo, - StringRef capturedExpression, - ResultDisposition::Flags resultDisposition ) - : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, - m_resultCapture( getResultCapture() ) - {} - - void AssertionHandler::handleExpr( ITransientExpression const& expr ) { - m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); - } - void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) { - m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction ); + if (m_reaction.shouldThrow) { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + throw Catch::TestFailureException(); +#else + CATCH_ERROR("Test failure requires aborting test!"); +#endif } + } + void AssertionHandler::setCompleted() { + m_completed = true; + } - auto AssertionHandler::allowThrows() const -> bool { - return getCurrentContext().getConfig()->allowThrows(); - } + void AssertionHandler::handleUnexpectedInflightException() { + m_resultCapture.handleUnexpectedInflightException(m_assertionInfo, Catch::translateActiveException(), m_reaction); + } - void AssertionHandler::complete() { - setCompleted(); - if( m_reaction.shouldDebugBreak ) { + void AssertionHandler::handleExceptionThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } + void AssertionHandler::handleExceptionNotThrownAsExpected() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } - // If you find your debugger stopping you here then go one level up on the - // call-stack for the code that caused it (typically a failed assertion) + void AssertionHandler::handleUnexpectedExceptionNotThrown() { + m_resultCapture.handleUnexpectedExceptionNotThrown(m_assertionInfo, m_reaction); + } - // (To go back to the test and change execution, jump over the throw, next) - CATCH_BREAK_INTO_DEBUGGER(); - } - if( m_reaction.shouldThrow ) - throw Catch::TestFailureException(); - } - void AssertionHandler::setCompleted() { - m_completed = true; - } + void AssertionHandler::handleThrowingCallSkipped() { + m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); + } - void AssertionHandler::handleUnexpectedInflightException() { - m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); - } - - void AssertionHandler::handleExceptionThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - void AssertionHandler::handleExceptionNotThrownAsExpected() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - - void AssertionHandler::handleUnexpectedExceptionNotThrown() { - m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction ); - } - - void AssertionHandler::handleThrowingCallSkipped() { - m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction); - } - - // This is the overload that takes a string and infers the Equals matcher from it - // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp - void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString ) { - handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString ); - } + // This is the overload that takes a string and infers the Equals matcher from it + // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp + void handleExceptionMatchExpr(AssertionHandler &handler, std::string const &str, StringRef const &matcherString) { + handleExceptionMatchExpr(handler, Matchers::Equals(str), matcherString); + } } // namespace Catch // end catch_assertionhandler.cpp // start catch_assertionresult.cpp namespace Catch { - AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression): - lazyExpression(_lazyExpression), - resultType(_resultType) {} + AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const &_lazyExpression) + : lazyExpression(_lazyExpression) + , resultType(_resultType) { + } - std::string AssertionResultData::reconstructExpression() const { - - if( reconstructedExpression.empty() ) { - if( lazyExpression ) { - ReusableStringStream rss; - rss << lazyExpression; - reconstructedExpression = rss.str(); - } - } - return reconstructedExpression; + std::string AssertionResultData::reconstructExpression() const { + if (reconstructedExpression.empty()) { + if (lazyExpression) { + ReusableStringStream rss; + rss << lazyExpression; + reconstructedExpression = rss.str(); + } } + return reconstructedExpression; + } - AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) - : m_info( info ), - m_resultData( data ) - {} + AssertionResult::AssertionResult(AssertionInfo const &info, AssertionResultData const &data) + : m_info(info) + , m_resultData(data) { + } - // Result was a success - bool AssertionResult::succeeded() const { - return Catch::isOk( m_resultData.resultType ); - } + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk(m_resultData.resultType); + } - // Result was a success, or failure is suppressed - bool AssertionResult::isOk() const { - return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); - } + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk(m_resultData.resultType) || shouldSuppressFailure(m_info.resultDisposition); + } - ResultWas::OfType AssertionResult::getResultType() const { - return m_resultData.resultType; - } + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } - bool AssertionResult::hasExpression() const { - return m_info.capturedExpression[0] != 0; - } + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } - bool AssertionResult::hasMessage() const { - return !m_resultData.message.empty(); - } + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } - std::string AssertionResult::getExpression() const { - if( isFalseTest( m_info.resultDisposition ) ) - return "!(" + m_info.capturedExpression + ")"; - else - return m_info.capturedExpression; + std::string AssertionResult::getExpression() const { + // Possibly overallocating by 3 characters should be basically free + std::string expr; + expr.reserve(m_info.capturedExpression.size() + 3); + if (isFalseTest(m_info.resultDisposition)) { + expr += "!("; } + expr += m_info.capturedExpression; + if (isFalseTest(m_info.resultDisposition)) { + expr += ')'; + } + return expr; + } - std::string AssertionResult::getExpressionInMacro() const { - std::string expr; - if( m_info.macroName[0] == 0 ) - expr = m_info.capturedExpression; - else { - expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; - } - return expr; + std::string AssertionResult::getExpressionInMacro() const { + std::string expr; + if (m_info.macroName.empty()) + expr = static_cast(m_info.capturedExpression); + else { + expr.reserve(m_info.macroName.size() + m_info.capturedExpression.size() + 4); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; } + return expr; + } - bool AssertionResult::hasExpandedExpression() const { - return hasExpression() && getExpandedExpression() != getExpression(); - } + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } - std::string AssertionResult::getExpandedExpression() const { - std::string expr = m_resultData.reconstructExpression(); - return expr.empty() - ? getExpression() - : expr; - } + std::string AssertionResult::getExpandedExpression() const { + std::string expr = m_resultData.reconstructExpression(); + return expr.empty() ? getExpression() : expr; + } - std::string AssertionResult::getMessage() const { - return m_resultData.message; - } - SourceLineInfo AssertionResult::getSourceInfo() const { - return m_info.lineInfo; - } + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } - StringRef AssertionResult::getTestMacroName() const { - return m_info.macroName; - } + StringRef AssertionResult::getTestMacroName() const { + return m_info.macroName; + } } // end namespace Catch // end catch_assertionresult.cpp -// start catch_benchmark.cpp - -namespace Catch { - - auto BenchmarkLooper::getResolution() -> uint64_t { - return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple(); - } - - void BenchmarkLooper::reportStart() { - getResultCapture().benchmarkStarting( { m_name } ); - } - auto BenchmarkLooper::needsMoreIterations() -> bool { - auto elapsed = m_timer.getElapsedNanoseconds(); - - // Exponentially increasing iterations until we're confident in our timer resolution - if( elapsed < m_resolution ) { - m_iterationsToRun *= 10; - return true; - } - - getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } ); - return false; - } - -} // end namespace Catch -// end catch_benchmark.cpp // start catch_capture_matchers.cpp namespace Catch { + using StringMatcher = Matchers::Impl::MatcherBase; - using StringMatcher = Matchers::Impl::MatcherBase; - - // This is the general overload that takes a any string matcher - // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers - // the Equals matcher (so the header does not mention matchers) - void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString ) { - std::string exceptionMessage = Catch::translateActiveException(); - MatchExpr expr( exceptionMessage, matcher, matcherString ); - handler.handleExpr( expr ); - } + // This is the general overload that takes a any string matcher + // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers + // the Equals matcher (so the header does not mention matchers) + void handleExceptionMatchExpr(AssertionHandler &handler, StringMatcher const &matcher, StringRef const &matcherString) { + std::string exceptionMessage = Catch::translateActiveException(); + MatchExpr expr(exceptionMessage, matcher, matcherString); + handler.handleExpr(expr); + } } // namespace Catch // end catch_capture_matchers.cpp @@ -5203,7 +8674,7 @@ namespace Catch { #define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH #endif -#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1 +#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH - 1 #ifdef __clang__ #pragma clang diagnostic push @@ -5220,8 +8691,7 @@ namespace Catch { // // See https://github.com/philsquared/Clara for more details -// Clara v1.1.4 - +// Clara v1.1.5 #ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH #define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80 @@ -5246,12 +8716,11 @@ namespace Catch { // // A single-header library for wrapping and laying out basic text, by Phil Nash // -// This work is licensed under the BSD 2-Clause license. -// See the accompanying LICENSE file, or the one at https://opensource.org/licenses/BSD-2-Clause +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // This project is hosted at https://github.com/philsquared/textflowcpp - #include #include #include @@ -5261,1197 +8730,1227 @@ namespace Catch { #define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 #endif -namespace Catch { namespace clara { namespace TextFlow { - - inline auto isWhitespace( char c ) -> bool { +namespace Catch { + namespace clara { + namespace TextFlow { + inline auto isWhitespace(char c) -> bool { static std::string chars = " \t\n\r"; - return chars.find( c ) != std::string::npos; - } - inline auto isBreakableBefore( char c ) -> bool { + return chars.find(c) != std::string::npos; + } + inline auto isBreakableBefore(char c) -> bool { static std::string chars = "[({<|"; - return chars.find( c ) != std::string::npos; - } - inline auto isBreakableAfter( char c ) -> bool { + return chars.find(c) != std::string::npos; + } + inline auto isBreakableAfter(char c) -> bool { static std::string chars = "])}>.,:;*+-=&/\\"; - return chars.find( c ) != std::string::npos; - } + return chars.find(c) != std::string::npos; + } - class Columns; + class Columns; - class Column { + class Column { std::vector m_strings; size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; size_t m_indent = 0; size_t m_initialIndent = std::string::npos; - public: + public: class iterator { - friend Column; + friend Column; - Column const& m_column; - size_t m_stringIndex = 0; - size_t m_pos = 0; + Column const &m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; - size_t m_len = 0; - size_t m_end = 0; - bool m_suffix = false; + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; - iterator( Column const& column, size_t stringIndex ) - : m_column( column ), - m_stringIndex( stringIndex ) - {} + iterator(Column const &column, size_t stringIndex) + : m_column(column) + , m_stringIndex(stringIndex) { + } - auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + auto line() const -> std::string const & { return m_column.m_strings[m_stringIndex]; } - auto isBoundary( size_t at ) const -> bool { - assert( at > 0 ); - assert( at <= line().size() ); + auto isBoundary(size_t at) const -> bool { + assert(at > 0); + assert(at <= line().size()); - return at == line().size() || - ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || - isBreakableBefore( line()[at] ) || - isBreakableAfter( line()[at-1] ); + return at == line().size() || (isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) || isBreakableBefore(line()[at]) + || isBreakableAfter(line()[at - 1]); + } + + void calcLength() { + assert(m_stringIndex < m_column.m_strings.size()); + + m_suffix = false; + auto width = m_column.m_width - indent(); + m_end = m_pos; + if (line()[m_pos] == '\n') { + ++m_end; } + while (m_end < line().size() && line()[m_end] != '\n') + ++m_end; - void calcLength() { - assert( m_stringIndex < m_column.m_strings.size() ); + if (m_end < m_pos + width) { + m_len = m_end - m_pos; + } else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace(line()[m_pos + len - 1])) + --len; - m_suffix = false; - auto width = m_column.m_width-indent(); - m_end = m_pos; - while( m_end < line().size() && line()[m_end] != '\n' ) - ++m_end; - - if( m_end < m_pos + width ) { - m_len = m_end - m_pos; - } - else { - size_t len = width; - while (len > 0 && !isBoundary(m_pos + len)) - --len; - while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) - --len; - - if (len > 0) { - m_len = len; - } else { - m_suffix = true; - m_len = width - 1; - } - } + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } } + } - auto indent() const -> size_t { - auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; - return initial == std::string::npos ? m_column.m_indent : initial; - } + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } - auto addIndentAndSuffix(std::string const &plain) const -> std::string { - return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); - } + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain); + } public: - explicit iterator( Column const& column ) : m_column( column ) { - assert( m_column.m_width > m_column.m_indent ); - assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); - calcLength(); - if( m_len == 0 ) - m_stringIndex++; // Empty string - } + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; - auto operator *() const -> std::string { - assert( m_stringIndex < m_column.m_strings.size() ); - assert( m_pos <= m_end ); - if( m_pos + m_column.m_width < m_end ) - return addIndentAndSuffix(line().substr(m_pos, m_len)); - else - return addIndentAndSuffix(line().substr(m_pos, m_end - m_pos)); - } + explicit iterator(Column const &column) + : m_column(column) { + assert(m_column.m_width > m_column.m_indent); + assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent); + calcLength(); + if (m_len == 0) + m_stringIndex++; // Empty string + } - auto operator ++() -> iterator& { - m_pos += m_len; - if( m_pos < line().size() && line()[m_pos] == '\n' ) - m_pos += 1; - else - while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) - ++m_pos; + auto operator*() const -> std::string { + assert(m_stringIndex < m_column.m_strings.size()); + assert(m_pos <= m_end); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } - if( m_pos == line().size() ) { - m_pos = 0; - ++m_stringIndex; - } - if( m_stringIndex < m_column.m_strings.size() ) - calcLength(); - return *this; - } - auto operator ++(int) -> iterator { - iterator prev( *this ); - operator++(); - return prev; - } + auto operator++() -> iterator & { + m_pos += m_len; + if (m_pos < line().size() && line()[m_pos] == '\n') + m_pos += 1; + else + while (m_pos < line().size() && isWhitespace(line()[m_pos])) + ++m_pos; - auto operator ==( iterator const& other ) const -> bool { - return - m_pos == other.m_pos && - m_stringIndex == other.m_stringIndex && - &m_column == &other.m_column; - } - auto operator !=( iterator const& other ) const -> bool { - return !operator==( other ); + if (m_pos == line().size()) { + m_pos = 0; + ++m_stringIndex; } + if (m_stringIndex < m_column.m_strings.size()) + calcLength(); + return *this; + } + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } + + auto operator==(iterator const &other) const -> bool { + return m_pos == other.m_pos && m_stringIndex == other.m_stringIndex && &m_column == &other.m_column; + } + auto operator!=(iterator const &other) const -> bool { return !operator==(other); } }; using const_iterator = iterator; - explicit Column( std::string const& text ) { m_strings.push_back( text ); } + explicit Column(std::string const &text) { m_strings.push_back(text); } - auto width( size_t newWidth ) -> Column& { - assert( newWidth > 0 ); - m_width = newWidth; - return *this; + auto width(size_t newWidth) -> Column & { + assert(newWidth > 0); + m_width = newWidth; + return *this; } - auto indent( size_t newIndent ) -> Column& { - m_indent = newIndent; - return *this; + auto indent(size_t newIndent) -> Column & { + m_indent = newIndent; + return *this; } - auto initialIndent( size_t newIndent ) -> Column& { - m_initialIndent = newIndent; - return *this; + auto initialIndent(size_t newIndent) -> Column & { + m_initialIndent = newIndent; + return *this; } auto width() const -> size_t { return m_width; } - auto begin() const -> iterator { return iterator( *this ); } - auto end() const -> iterator { return { *this, m_strings.size() }; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, m_strings.size()}; } - inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { - bool first = true; - for( auto line : col ) { - if( first ) - first = false; - else - os << "\n"; - os << line; - } - return os; + inline friend std::ostream &operator<<(std::ostream &os, Column const &col) { + bool first = true; + for (auto line : col) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; } - auto operator + ( Column const& other ) -> Columns; + auto operator+(Column const &other) -> Columns; auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); + std::ostringstream oss; + oss << *this; + return oss.str(); } - }; + }; - class Spacer : public Column { - - public: - explicit Spacer( size_t spaceWidth ) : Column( "" ) { - width( spaceWidth ); + class Spacer : public Column { + public: + explicit Spacer(size_t spaceWidth) + : Column("") { + width(spaceWidth); } - }; + }; - class Columns { + class Columns { std::vector m_columns; - public: - + public: class iterator { - friend Columns; - struct EndTag {}; + friend Columns; + struct EndTag { + }; - std::vector const& m_columns; - std::vector m_iterators; - size_t m_activeIterators; + std::vector const &m_columns; + std::vector m_iterators; + size_t m_activeIterators; - iterator( Columns const& columns, EndTag ) - : m_columns( columns.m_columns ), - m_activeIterators( 0 ) - { - m_iterators.reserve( m_columns.size() ); + iterator(Columns const &columns, EndTag) + : m_columns(columns.m_columns) + , m_activeIterators(0) { + m_iterators.reserve(m_columns.size()); - for( auto const& col : m_columns ) - m_iterators.push_back( col.end() ); - } + for (auto const &col : m_columns) + m_iterators.push_back(col.end()); + } public: - explicit iterator( Columns const& columns ) - : m_columns( columns.m_columns ), - m_activeIterators( m_columns.size() ) - { - m_iterators.reserve( m_columns.size() ); + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type *; + using reference = value_type &; + using iterator_category = std::forward_iterator_tag; - for( auto const& col : m_columns ) - m_iterators.push_back( col.begin() ); - } + explicit iterator(Columns const &columns) + : m_columns(columns.m_columns) + , m_activeIterators(m_columns.size()) { + m_iterators.reserve(m_columns.size()); - auto operator ==( iterator const& other ) const -> bool { - return m_iterators == other.m_iterators; - } - auto operator !=( iterator const& other ) const -> bool { - return m_iterators != other.m_iterators; - } - auto operator *() const -> std::string { - std::string row, padding; + for (auto const &col : m_columns) + m_iterators.push_back(col.begin()); + } - for( size_t i = 0; i < m_columns.size(); ++i ) { - auto width = m_columns[i].width(); - if( m_iterators[i] != m_columns[i].end() ) { - std::string col = *m_iterators[i]; - row += padding + col; - if( col.size() < width ) - padding = std::string( width - col.size(), ' ' ); - else - padding = ""; - } - else { - padding += std::string( width, ' ' ); - } - } - return row; + auto operator==(iterator const &other) const -> bool { return m_iterators == other.m_iterators; } + auto operator!=(iterator const &other) const -> bool { return m_iterators != other.m_iterators; } + auto operator*() const -> std::string { + std::string row, padding; + + for (size_t i = 0; i < m_columns.size(); ++i) { + auto width = m_columns[i].width(); + if (m_iterators[i] != m_columns[i].end()) { + std::string col = *m_iterators[i]; + row += padding + col; + if (col.size() < width) + padding = std::string(width - col.size(), ' '); + else + padding = ""; + } else { + padding += std::string(width, ' '); + } } - auto operator ++() -> iterator& { - for( size_t i = 0; i < m_columns.size(); ++i ) { - if (m_iterators[i] != m_columns[i].end()) - ++m_iterators[i]; - } - return *this; - } - auto operator ++(int) -> iterator { - iterator prev( *this ); - operator++(); - return prev; + return row; + } + auto operator++() -> iterator & { + for (size_t i = 0; i < m_columns.size(); ++i) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; } + return *this; + } + auto operator++(int) -> iterator { + iterator prev(*this); + operator++(); + return prev; + } }; using const_iterator = iterator; - auto begin() const -> iterator { return iterator( *this ); } - auto end() const -> iterator { return { *this, iterator::EndTag() }; } + auto begin() const -> iterator { return iterator(*this); } + auto end() const -> iterator { return {*this, iterator::EndTag()}; } - auto operator += ( Column const& col ) -> Columns& { - m_columns.push_back( col ); - return *this; + auto operator+=(Column const &col) -> Columns & { + m_columns.push_back(col); + return *this; } - auto operator + ( Column const& col ) -> Columns { - Columns combined = *this; - combined += col; - return combined; + auto operator+(Column const &col) -> Columns { + Columns combined = *this; + combined += col; + return combined; } - inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { - - bool first = true; - for( auto line : cols ) { - if( first ) - first = false; - else - os << "\n"; - os << line; - } - return os; + inline friend std::ostream &operator<<(std::ostream &os, Columns const &cols) { + bool first = true; + for (auto line : cols) { + if (first) + first = false; + else + os << "\n"; + os << line; + } + return os; } auto toString() const -> std::string { - std::ostringstream oss; - oss << *this; - return oss.str(); + std::ostringstream oss; + oss << *this; + return oss.str(); } - }; + }; - inline auto Column::operator + ( Column const& other ) -> Columns { + inline auto Column::operator+(Column const &other) -> Columns { Columns cols; cols += *this; cols += other; return cols; - } -}}} // namespace Catch::clara::TextFlow + } + } // namespace TextFlow + } // namespace clara +} // namespace Catch // ----------- end of #include from clara_textflow.hpp ----------- // ........... back in clara.hpp +#include +#include #include #include -#include +#include -#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#if !defined(CATCH_PLATFORM_WINDOWS) && (defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)) #define CATCH_PLATFORM_WINDOWS #endif -namespace Catch { namespace clara { -namespace detail { +namespace Catch { + namespace clara { + namespace detail { + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits { + }; - // Traits for extracting arg and return type of lambdas (for single argument lambdas) - template - struct UnaryLambdaTraits : UnaryLambdaTraits {}; - - template - struct UnaryLambdaTraits { + template + struct UnaryLambdaTraits { static const bool isValid = false; - }; + }; - template - struct UnaryLambdaTraits { + template + struct UnaryLambdaTraits { static const bool isValid = true; using ArgType = typename std::remove_const::type>::type; using ReturnType = ReturnT; - }; + }; - class TokenStream; + class TokenStream; - // Transport for raw args (copied from main args, or supplied via init list for testing) - class Args { + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { friend TokenStream; std::string m_exeName; std::vector m_args; - public: - Args( int argc, char const* const* argv ) - : m_exeName(argv[0]), - m_args(argv + 1, argv + argc) {} - - Args( std::initializer_list args ) - : m_exeName( *args.begin() ), - m_args( args.begin()+1, args.end() ) - {} - - auto exeName() const -> std::string { - return m_exeName; + public: + Args(int argc, char const *const *argv) + : m_exeName(argv[0]) + , m_args(argv + 1, argv + argc) { } - }; - // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string - // may encode an option + its argument if the : or = form is used - enum class TokenType { - Option, Argument - }; - struct Token { + Args(std::initializer_list args) + : m_exeName(*args.begin()) + , m_args(args.begin() + 1, args.end()) { + } + + auto exeName() const -> std::string { return m_exeName; } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, + Argument + }; + struct Token { TokenType type; std::string token; - }; + }; - inline auto isOptPrefix( char c ) -> bool { + inline auto isOptPrefix(char c) -> bool { return c == '-' #ifdef CATCH_PLATFORM_WINDOWS || c == '/' #endif - ; - } + ; + } - // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled - class TokenStream { + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { using Iterator = std::vector::const_iterator; Iterator it; Iterator itEnd; std::vector m_tokenBuffer; void loadBuffer() { - m_tokenBuffer.resize( 0 ); + m_tokenBuffer.resize(0); - // Skip any empty strings - while( it != itEnd && it->empty() ) - ++it; + // Skip any empty strings + while (it != itEnd && it->empty()) + ++it; - if( it != itEnd ) { - auto const &next = *it; - if( isOptPrefix( next[0] ) ) { - auto delimiterPos = next.find_first_of( " :=" ); - if( delimiterPos != std::string::npos ) { - m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); - m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); - } else { - if( next[1] != '-' && next.size() > 2 ) { - std::string opt = "- "; - for( size_t i = 1; i < next.size(); ++i ) { - opt[1] = next[i]; - m_tokenBuffer.push_back( { TokenType::Option, opt } ); - } - } else { - m_tokenBuffer.push_back( { TokenType::Option, next } ); - } - } + if (it != itEnd) { + auto const &next = *it; + if (isOptPrefix(next[0])) { + auto delimiterPos = next.find_first_of(" :="); + if (delimiterPos != std::string::npos) { + m_tokenBuffer.push_back({TokenType::Option, next.substr(0, delimiterPos)}); + m_tokenBuffer.push_back({TokenType::Argument, next.substr(delimiterPos + 1)}); + } else { + if (next[1] != '-' && next.size() > 2) { + std::string opt = "- "; + for (size_t i = 1; i < next.size(); ++i) { + opt[1] = next[i]; + m_tokenBuffer.push_back({TokenType::Option, opt}); + } } else { - m_tokenBuffer.push_back( { TokenType::Argument, next } ); + m_tokenBuffer.push_back({TokenType::Option, next}); } + } + } else { + m_tokenBuffer.push_back({TokenType::Argument, next}); } + } } - public: - explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} - - TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { - loadBuffer(); + public: + explicit TokenStream(Args const &args) + : TokenStream(args.m_args.begin(), args.m_args.end()) { } - explicit operator bool() const { - return !m_tokenBuffer.empty() || it != itEnd; + TokenStream(Iterator it, Iterator itEnd) + : it(it) + , itEnd(itEnd) { + loadBuffer(); } + explicit operator bool() const { return !m_tokenBuffer.empty() || it != itEnd; } + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } auto operator*() const -> Token { - assert( !m_tokenBuffer.empty() ); - return m_tokenBuffer.front(); + assert(!m_tokenBuffer.empty()); + return m_tokenBuffer.front(); } auto operator->() const -> Token const * { - assert( !m_tokenBuffer.empty() ); - return &m_tokenBuffer.front(); + assert(!m_tokenBuffer.empty()); + return &m_tokenBuffer.front(); } auto operator++() -> TokenStream & { - if( m_tokenBuffer.size() >= 2 ) { - m_tokenBuffer.erase( m_tokenBuffer.begin() ); - } else { - if( it != itEnd ) - ++it; - loadBuffer(); - } - return *this; + if (m_tokenBuffer.size() >= 2) { + m_tokenBuffer.erase(m_tokenBuffer.begin()); + } else { + if (it != itEnd) + ++it; + loadBuffer(); + } + return *this; } - }; + }; - class ResultBase { - public: + class ResultBase { + public: enum Type { - Ok, LogicError, RuntimeError + Ok, + LogicError, + RuntimeError }; - protected: - ResultBase( Type type ) : m_type( type ) {} + protected: + ResultBase(Type type) + : m_type(type) { + } virtual ~ResultBase() = default; virtual void enforceOk() const = 0; Type m_type; - }; + }; - template - class ResultValueBase : public ResultBase { - public: + template + class ResultValueBase : public ResultBase { + public: auto value() const -> T const & { - enforceOk(); - return m_value; + enforceOk(); + return m_value; } - protected: - ResultValueBase( Type type ) : ResultBase( type ) {} - - ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { - if( m_type == ResultBase::Ok ) - new( &m_value ) T( other.m_value ); + protected: + ResultValueBase(Type type) + : ResultBase(type) { } - ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { - new( &m_value ) T( value ); + ResultValueBase(ResultValueBase const &other) + : ResultBase(other) { + if (m_type == ResultBase::Ok) + new (&m_value) T(other.m_value); } - auto operator=( ResultValueBase const &other ) -> ResultValueBase & { - if( m_type == ResultBase::Ok ) - m_value.~T(); - ResultBase::operator=(other); - if( m_type == ResultBase::Ok ) - new( &m_value ) T( other.m_value ); - return *this; + ResultValueBase(Type, T const &value) + : ResultBase(Ok) { + new (&m_value) T(value); + } + + auto operator=(ResultValueBase const &other) -> ResultValueBase & { + if (m_type == ResultBase::Ok) + m_value.~T(); + ResultBase::operator=(other); + if (m_type == ResultBase::Ok) + new (&m_value) T(other.m_value); + return *this; } ~ResultValueBase() override { - if( m_type == Ok ) - m_value.~T(); + if (m_type == Ok) + m_value.~T(); } union { - T m_value; + T m_value; }; - }; + }; - template<> - class ResultValueBase : public ResultBase { - protected: + template<> + class ResultValueBase : public ResultBase { + protected: using ResultBase::ResultBase; - }; + }; - template - class BasicResult : public ResultValueBase { - public: + template + class BasicResult : public ResultValueBase { + public: template - explicit BasicResult( BasicResult const &other ) - : ResultValueBase( other.type() ), - m_errorMessage( other.errorMessage() ) - { - assert( type() != ResultBase::Ok ); + explicit BasicResult(BasicResult const &other) + : ResultValueBase(other.type()) + , m_errorMessage(other.errorMessage()) { + assert(type() != ResultBase::Ok); } template - static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } - static auto ok() -> BasicResult { return { ResultBase::Ok }; } - static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } - static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + static auto ok(U const &value) -> BasicResult { + return {ResultBase::Ok, value}; + } + static auto ok() -> BasicResult { return {ResultBase::Ok}; } + static auto logicError(std::string const &message) -> BasicResult { return {ResultBase::LogicError, message}; } + static auto runtimeError(std::string const &message) -> BasicResult { return {ResultBase::RuntimeError, message}; } explicit operator bool() const { return m_type == ResultBase::Ok; } auto type() const -> ResultBase::Type { return m_type; } auto errorMessage() const -> std::string { return m_errorMessage; } - protected: + protected: void enforceOk() const override { - - // Errors shouldn't reach this point, but if they do - // the actual error message will be in m_errorMessage - assert( m_type != ResultBase::LogicError ); - assert( m_type != ResultBase::RuntimeError ); - if( m_type != ResultBase::Ok ) - std::abort(); + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert(m_type != ResultBase::LogicError); + assert(m_type != ResultBase::RuntimeError); + if (m_type != ResultBase::Ok) + std::abort(); } std::string m_errorMessage; // Only populated if resultType is an error - BasicResult( ResultBase::Type type, std::string const &message ) - : ResultValueBase(type), - m_errorMessage(message) - { - assert( m_type != ResultBase::Ok ); + BasicResult(ResultBase::Type type, std::string const &message) + : ResultValueBase(type) + , m_errorMessage(message) { + assert(m_type != ResultBase::Ok); } using ResultValueBase::ResultValueBase; using ResultBase::m_type; - }; + }; - enum class ParseResultType { - Matched, NoMatch, ShortCircuitAll, ShortCircuitSame - }; + enum class ParseResultType { + Matched, + NoMatch, + ShortCircuitAll, + ShortCircuitSame + }; - class ParseState { - public: - - ParseState( ParseResultType type, TokenStream const &remainingTokens ) - : m_type(type), - m_remainingTokens( remainingTokens ) - {} + class ParseState { + public: + ParseState(ParseResultType type, TokenStream const &remainingTokens) + : m_type(type) + , m_remainingTokens(remainingTokens) { + } auto type() const -> ParseResultType { return m_type; } auto remainingTokens() const -> TokenStream { return m_remainingTokens; } - private: + private: ParseResultType m_type; TokenStream m_remainingTokens; - }; + }; - using Result = BasicResult; - using ParserResult = BasicResult; - using InternalParseResult = BasicResult; + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; - struct HelpColumns { + struct HelpColumns { std::string left; std::string right; - }; + }; - template - inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + template + inline auto convertInto(std::string const &source, T &target) -> ParserResult { std::stringstream ss; ss << source; ss >> target; - if( ss.fail() ) - return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + if (ss.fail()) + return ParserResult::runtimeError("Unable to convert '" + source + "' to destination type"); else - return ParserResult::ok( ParseResultType::Matched ); - } - inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + return ParserResult::ok(ParseResultType::Matched); + } + inline auto convertInto(std::string const &source, std::string &target) -> ParserResult { target = source; - return ParserResult::ok( ParseResultType::Matched ); - } - inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + return ParserResult::ok(ParseResultType::Matched); + } + inline auto convertInto(std::string const &source, bool &target) -> ParserResult { std::string srcLC = source; - std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + std::transform(srcLC.begin(), srcLC.end(), srcLC.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") - target = true; + target = true; else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") - target = false; + target = false; else - return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); - return ParserResult::ok( ParseResultType::Matched ); - } + return ParserResult::runtimeError("Expected a boolean value but did not recognise: '" + source + "'"); + return ParserResult::ok(ParseResultType::Matched); + } #ifdef CLARA_CONFIG_OPTIONAL_TYPE - template - inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + template + inline auto convertInto(std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE &target) -> ParserResult { T temp; - auto result = convertInto( source, temp ); - if( result ) - target = std::move(temp); + auto result = convertInto(source, temp); + if (result) + target = std::move(temp); return result; - } + } #endif // CLARA_CONFIG_OPTIONAL_TYPE - struct NonCopyable { + struct NonCopyable { NonCopyable() = default; - NonCopyable( NonCopyable const & ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable &operator=( NonCopyable const & ) = delete; - NonCopyable &operator=( NonCopyable && ) = delete; - }; + NonCopyable(NonCopyable const &) = delete; + NonCopyable(NonCopyable &&) = delete; + NonCopyable &operator=(NonCopyable const &) = delete; + NonCopyable &operator=(NonCopyable &&) = delete; + }; - struct BoundRef : NonCopyable { + struct BoundRef : NonCopyable { virtual ~BoundRef() = default; virtual auto isContainer() const -> bool { return false; } virtual auto isFlag() const -> bool { return false; } - }; - struct BoundValueRefBase : BoundRef { - virtual auto setValue( std::string const &arg ) -> ParserResult = 0; - }; - struct BoundFlagRefBase : BoundRef { - virtual auto setFlag( bool flag ) -> ParserResult = 0; + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue(std::string const &arg) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag(bool flag) -> ParserResult = 0; virtual auto isFlag() const -> bool { return true; } - }; + }; - template - struct BoundValueRef : BoundValueRefBase { + template + struct BoundValueRef : BoundValueRefBase { T &m_ref; - explicit BoundValueRef( T &ref ) : m_ref( ref ) {} - - auto setValue( std::string const &arg ) -> ParserResult override { - return convertInto( arg, m_ref ); + explicit BoundValueRef(T &ref) + : m_ref(ref) { } - }; - template - struct BoundValueRef> : BoundValueRefBase { + auto setValue(std::string const &arg) -> ParserResult override { return convertInto(arg, m_ref); } + }; + + template + struct BoundValueRef> : BoundValueRefBase { std::vector &m_ref; - explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + explicit BoundValueRef(std::vector &ref) + : m_ref(ref) { + } auto isContainer() const -> bool override { return true; } - auto setValue( std::string const &arg ) -> ParserResult override { - T temp; - auto result = convertInto( arg, temp ); - if( result ) - m_ref.push_back( temp ); - return result; + auto setValue(std::string const &arg) -> ParserResult override { + T temp; + auto result = convertInto(arg, temp); + if (result) + m_ref.push_back(temp); + return result; } - }; + }; - struct BoundFlagRef : BoundFlagRefBase { + struct BoundFlagRef : BoundFlagRefBase { bool &m_ref; - explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} - - auto setFlag( bool flag ) -> ParserResult override { - m_ref = flag; - return ParserResult::ok( ParseResultType::Matched ); + explicit BoundFlagRef(bool &ref) + : m_ref(ref) { } - }; - template - struct LambdaInvoker { - static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + auto setFlag(bool flag) -> ParserResult override { + m_ref = flag; + return ParserResult::ok(ParseResultType::Matched); + } + }; + + template + struct LambdaInvoker { + static_assert(std::is_same::value, "Lambda must return void or clara::ParserResult"); template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - return lambda( arg ); + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { + return lambda(arg); } - }; + }; - template<> - struct LambdaInvoker { + template<> + struct LambdaInvoker { template - static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { - lambda( arg ); - return ParserResult::ok( ParseResultType::Matched ); + static auto invoke(L const &lambda, ArgType const &arg) -> ParserResult { + lambda(arg); + return ParserResult::ok(ParseResultType::Matched); } - }; + }; - template - inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + template + inline auto invokeLambda(L const &lambda, std::string const &arg) -> ParserResult { ArgType temp{}; - auto result = convertInto( arg, temp ); - return !result - ? result - : LambdaInvoker::ReturnType>::invoke( lambda, temp ); - } + auto result = convertInto(arg, temp); + return !result ? result : LambdaInvoker::ReturnType>::invoke(lambda, temp); + } - template - struct BoundLambda : BoundValueRefBase { + template + struct BoundLambda : BoundValueRefBase { L m_lambda; - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} - - auto setValue( std::string const &arg ) -> ParserResult override { - return invokeLambda::ArgType>( m_lambda, arg ); + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + explicit BoundLambda(L const &lambda) + : m_lambda(lambda) { } - }; - template - struct BoundFlagLambda : BoundFlagRefBase { + auto setValue(std::string const &arg) -> ParserResult override { + return invokeLambda::ArgType>(m_lambda, arg); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { L m_lambda; - static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); - static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + static_assert(UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument"); + static_assert(std::is_same::ArgType, bool>::value, "flags must be boolean"); - explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} - - auto setFlag( bool flag ) -> ParserResult override { - return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + explicit BoundFlagLambda(L const &lambda) + : m_lambda(lambda) { } - }; - enum class Optionality { Optional, Required }; + auto setFlag(bool flag) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke(m_lambda, flag); + } + }; - struct Parser; + enum class Optionality { + Optional, + Required + }; - class ParserBase { - public: + struct Parser; + + class ParserBase { + public: virtual ~ParserBase() = default; virtual auto validate() const -> Result { return Result::ok(); } - virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult = 0; virtual auto cardinality() const -> size_t { return 1; } - auto parse( Args const &args ) const -> InternalParseResult { - return parse( args.exeName(), TokenStream( args ) ); - } - }; + auto parse(Args const &args) const -> InternalParseResult { return parse(args.exeName(), TokenStream(args)); } + }; - template - class ComposableParserImpl : public ParserBase { - public: + template + class ComposableParserImpl : public ParserBase { + public: template - auto operator|( T const &other ) const -> Parser; + auto operator|(T const &other) const -> Parser; - template - auto operator+( T const &other ) const -> Parser; - }; + template + auto operator+(T const &other) const -> Parser; + }; - // Common code and state for Args and Opts - template - class ParserRefImpl : public ComposableParserImpl { - protected: + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: Optionality m_optionality = Optionality::Optional; std::shared_ptr m_ref; std::string m_hint; std::string m_description; - explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + explicit ParserRefImpl(std::shared_ptr const &ref) + : m_ref(ref) { + } - public: + public: template - ParserRefImpl( T &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint( hint ) - {} + ParserRefImpl(T &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)) + , m_hint(hint) { + } template - ParserRefImpl( LambdaT const &ref, std::string const &hint ) - : m_ref( std::make_shared>( ref ) ), - m_hint(hint) - {} + ParserRefImpl(LambdaT const &ref, std::string const &hint) + : m_ref(std::make_shared>(ref)) + , m_hint(hint) { + } - auto operator()( std::string const &description ) -> DerivedT & { - m_description = description; - return static_cast( *this ); + auto operator()(std::string const &description) -> DerivedT & { + m_description = description; + return static_cast(*this); } auto optional() -> DerivedT & { - m_optionality = Optionality::Optional; - return static_cast( *this ); + m_optionality = Optionality::Optional; + return static_cast(*this); }; auto required() -> DerivedT & { - m_optionality = Optionality::Required; - return static_cast( *this ); + m_optionality = Optionality::Required; + return static_cast(*this); }; - auto isOptional() const -> bool { - return m_optionality == Optionality::Optional; - } + auto isOptional() const -> bool { return m_optionality == Optionality::Optional; } auto cardinality() const -> size_t override { - if( m_ref->isContainer() ) - return 0; - else - return 1; + if (m_ref->isContainer()) + return 0; + else + return 1; } auto hint() const -> std::string { return m_hint; } - }; + }; - class ExeName : public ComposableParserImpl { + class ExeName : public ComposableParserImpl { std::shared_ptr m_name; std::shared_ptr m_ref; template static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { - return std::make_shared>( lambda) ; + return std::make_shared>(lambda); } - public: - ExeName() : m_name( std::make_shared( "" ) ) {} + public: + ExeName() + : m_name(std::make_shared("")) { + } - explicit ExeName( std::string &ref ) : ExeName() { - m_ref = std::make_shared>( ref ); + explicit ExeName(std::string &ref) + : ExeName() { + m_ref = std::make_shared>(ref); } template - explicit ExeName( LambdaT const& lambda ) : ExeName() { - m_ref = std::make_shared>( lambda ); + explicit ExeName(LambdaT const &lambda) + : ExeName() { + m_ref = std::make_shared>(lambda); } // The exe name is not parsed out of the normal tokens, but is handled specially - auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); } auto name() const -> std::string { return *m_name; } - auto set( std::string const& newName ) -> ParserResult { + auto set(std::string const &newName) -> ParserResult { + auto lastSlash = newName.find_last_of("\\/"); + auto filename = (lastSlash == std::string::npos) ? newName : newName.substr(lastSlash + 1); - auto lastSlash = newName.find_last_of( "\\/" ); - auto filename = ( lastSlash == std::string::npos ) - ? newName - : newName.substr( lastSlash+1 ); - - *m_name = filename; - if( m_ref ) - return m_ref->setValue( filename ); - else - return ParserResult::ok( ParseResultType::Matched ); + *m_name = filename; + if (m_ref) + return m_ref->setValue(filename); + else + return ParserResult::ok(ParseResultType::Matched); } - }; + }; - class Arg : public ParserRefImpl { - public: + class Arg : public ParserRefImpl { + public: using ParserRefImpl::ParserRefImpl; - auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { - auto validationResult = validate(); - if( !validationResult ) - return InternalParseResult( validationResult ); + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); - auto remainingTokens = tokens; - auto const &token = *remainingTokens; - if( token.type != TokenType::Argument ) - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if (token.type != TokenType::Argument) + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); - assert( !m_ref->isFlag() ); - auto valueRef = static_cast( m_ref.get() ); + assert(!m_ref->isFlag()); + auto valueRef = static_cast(m_ref.get()); - auto result = valueRef->setValue( remainingTokens->token ); - if( !result ) - return InternalParseResult( result ); - else - return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + auto result = valueRef->setValue(remainingTokens->token); + if (!result) + return InternalParseResult(result); + else + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); } - }; + }; - inline auto normaliseOpt( std::string const &optName ) -> std::string { + inline auto normaliseOpt(std::string const &optName) -> std::string { #ifdef CATCH_PLATFORM_WINDOWS - if( optName[0] == '/' ) - return "-" + optName.substr( 1 ); + if (optName[0] == '/') + return "-" + optName.substr(1); else #endif - return optName; - } + return optName; + } - class Opt : public ParserRefImpl { - protected: + class Opt : public ParserRefImpl { + protected: std::vector m_optNames; - public: + public: template - explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + explicit Opt(LambdaT const &ref) + : ParserRefImpl(std::make_shared>(ref)) { + } - explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + explicit Opt(bool &ref) + : ParserRefImpl(std::make_shared(ref)) { + } template - Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + Opt(LambdaT const &ref, std::string const &hint) + : ParserRefImpl(ref, hint) { + } template - Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + Opt(T &ref, std::string const &hint) + : ParserRefImpl(ref, hint) { + } - auto operator[]( std::string const &optName ) -> Opt & { - m_optNames.push_back( optName ); - return *this; + auto operator[](std::string const &optName) -> Opt & { + m_optNames.push_back(optName); + return *this; } auto getHelpColumns() const -> std::vector { - std::ostringstream oss; - bool first = true; - for( auto const &opt : m_optNames ) { - if (first) - first = false; - else - oss << ", "; - oss << opt; - } - if( !m_hint.empty() ) - oss << " <" << m_hint << ">"; - return { { oss.str(), m_description } }; + std::ostringstream oss; + bool first = true; + for (auto const &opt : m_optNames) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if (!m_hint.empty()) + oss << " <" << m_hint << ">"; + return {{oss.str(), m_description}}; } - auto isMatch( std::string const &optToken ) const -> bool { - auto normalisedToken = normaliseOpt( optToken ); - for( auto const &name : m_optNames ) { - if( normaliseOpt( name ) == normalisedToken ) - return true; - } - return false; + auto isMatch(std::string const &optToken) const -> bool { + auto normalisedToken = normaliseOpt(optToken); + for (auto const &name : m_optNames) { + if (normaliseOpt(name) == normalisedToken) + return true; + } + return false; } using ParserBase::parse; - auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { - auto validationResult = validate(); - if( !validationResult ) - return InternalParseResult( validationResult ); + auto parse(std::string const &, TokenStream const &tokens) const -> InternalParseResult override { + auto validationResult = validate(); + if (!validationResult) + return InternalParseResult(validationResult); - auto remainingTokens = tokens; - if( remainingTokens && remainingTokens->type == TokenType::Option ) { - auto const &token = *remainingTokens; - if( isMatch(token.token ) ) { - if( m_ref->isFlag() ) { - auto flagRef = static_cast( m_ref.get() ); - auto result = flagRef->setFlag( true ); - if( !result ) - return InternalParseResult( result ); - if( result.value() == ParseResultType::ShortCircuitAll ) - return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); - } else { - auto valueRef = static_cast( m_ref.get() ); - ++remainingTokens; - if( !remainingTokens ) - return InternalParseResult::runtimeError( "Expected argument following " + token.token ); - auto const &argToken = *remainingTokens; - if( argToken.type != TokenType::Argument ) - return InternalParseResult::runtimeError( "Expected argument following " + token.token ); - auto result = valueRef->setValue( argToken.token ); - if( !result ) - return InternalParseResult( result ); - if( result.value() == ParseResultType::ShortCircuitAll ) - return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); - } - return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); - } + auto remainingTokens = tokens; + if (remainingTokens && remainingTokens->type == TokenType::Option) { + auto const &token = *remainingTokens; + if (isMatch(token.token)) { + if (m_ref->isFlag()) { + auto flagRef = static_cast(m_ref.get()); + auto result = flagRef->setFlag(true); + if (!result) + return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } else { + auto valueRef = static_cast(m_ref.get()); + ++remainingTokens; + if (!remainingTokens) + return InternalParseResult::runtimeError("Expected argument following " + token.token); + auto const &argToken = *remainingTokens; + if (argToken.type != TokenType::Argument) + return InternalParseResult::runtimeError("Expected argument following " + token.token); + auto result = valueRef->setValue(argToken.token); + if (!result) + return InternalParseResult(result); + if (result.value() == ParseResultType::ShortCircuitAll) + return InternalParseResult::ok(ParseState(result.value(), remainingTokens)); + } + return InternalParseResult::ok(ParseState(ParseResultType::Matched, ++remainingTokens)); } - return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + return InternalParseResult::ok(ParseState(ParseResultType::NoMatch, remainingTokens)); } auto validate() const -> Result override { - if( m_optNames.empty() ) - return Result::logicError( "No options supplied to Opt" ); - for( auto const &name : m_optNames ) { - if( name.empty() ) - return Result::logicError( "Option name cannot be empty" ); + if (m_optNames.empty()) + return Result::logicError("No options supplied to Opt"); + for (auto const &name : m_optNames) { + if (name.empty()) + return Result::logicError("Option name cannot be empty"); #ifdef CATCH_PLATFORM_WINDOWS - if( name[0] != '-' && name[0] != '/' ) - return Result::logicError( "Option name must begin with '-' or '/'" ); + if (name[0] != '-' && name[0] != '/') + return Result::logicError("Option name must begin with '-' or '/'"); #else - if( name[0] != '-' ) - return Result::logicError( "Option name must begin with '-'" ); + if (name[0] != '-') + return Result::logicError("Option name must begin with '-'"); #endif - } - return ParserRefImpl::validate(); + } + return ParserRefImpl::validate(); } - }; + }; - struct Help : Opt { - Help( bool &showHelpFlag ) - : Opt([&]( bool flag ) { - showHelpFlag = flag; - return ParserResult::ok( ParseResultType::ShortCircuitAll ); - }) - { - static_cast( *this ) - ("display usage information") - ["-?"]["-h"]["--help"] - .optional(); + struct Help : Opt { + Help(bool &showHelpFlag) + : Opt([&](bool flag) { + showHelpFlag = flag; + return ParserResult::ok(ParseResultType::ShortCircuitAll); + }) { + static_cast (*this)("display usage information")["-?"]["-h"]["--help"].optional(); } - }; - - struct Parser : ParserBase { + }; + struct Parser : ParserBase { mutable ExeName m_exeName; std::vector m_options; std::vector m_args; - auto operator|=( ExeName const &exeName ) -> Parser & { - m_exeName = exeName; - return *this; + auto operator|=(ExeName const &exeName) -> Parser & { + m_exeName = exeName; + return *this; } - auto operator|=( Arg const &arg ) -> Parser & { - m_args.push_back(arg); - return *this; + auto operator|=(Arg const &arg) -> Parser & { + m_args.push_back(arg); + return *this; } - auto operator|=( Opt const &opt ) -> Parser & { - m_options.push_back(opt); - return *this; + auto operator|=(Opt const &opt) -> Parser & { + m_options.push_back(opt); + return *this; } - auto operator|=( Parser const &other ) -> Parser & { - m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); - m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); - return *this; + auto operator|=(Parser const &other) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; } template - auto operator|( T const &other ) const -> Parser { - return Parser( *this ) |= other; + auto operator|(T const &other) const -> Parser { + return Parser(*this) |= other; } // Forward deprecated interface with '+' instead of '|' template - auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + auto operator+=(T const &other) -> Parser & { + return operator|=(other); + } template - auto operator+( T const &other ) const -> Parser { return operator|( other ); } + auto operator+(T const &other) const -> Parser { + return operator|(other); + } auto getHelpColumns() const -> std::vector { - std::vector cols; - for (auto const &o : m_options) { - auto childCols = o.getHelpColumns(); - cols.insert( cols.end(), childCols.begin(), childCols.end() ); - } - return cols; + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert(cols.end(), childCols.begin(), childCols.end()); + } + return cols; } - void writeToStream( std::ostream &os ) const { - if (!m_exeName.name().empty()) { - os << "usage:\n" << " " << m_exeName.name() << " "; - bool required = true, first = true; - for( auto const &arg : m_args ) { - if (first) - first = false; - else - os << " "; - if( arg.isOptional() && required ) { - os << "["; - required = false; - } - os << "<" << arg.hint() << ">"; - if( arg.cardinality() == 0 ) - os << " ... "; - } - if( !required ) - os << "]"; - if( !m_options.empty() ) - os << " options"; - os << "\n\nwhere options are:" << std::endl; + void writeToStream(std::ostream &os) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" + << " " << m_exeName.name() << " "; + bool required = true, first = true; + for (auto const &arg : m_args) { + if (first) + first = false; + else + os << " "; + if (arg.isOptional() && required) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if (arg.cardinality() == 0) + os << " ... "; } + if (!required) + os << "]"; + if (!m_options.empty()) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } - auto rows = getHelpColumns(); - size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; - size_t optWidth = 0; - for( auto const &cols : rows ) - optWidth = (std::max)(optWidth, cols.left.size() + 2); + auto rows = getHelpColumns(); + size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for (auto const &cols : rows) + optWidth = (std::max)(optWidth, cols.left.size() + 2); - optWidth = (std::min)(optWidth, consoleWidth/2); + optWidth = (std::min)(optWidth, consoleWidth / 2); - for( auto const &cols : rows ) { - auto row = - TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + - TextFlow::Spacer(4) + - TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); - os << row << std::endl; - } + for (auto const &cols : rows) { + auto row = TextFlow::Column(cols.left).width(optWidth).indent(2) + TextFlow::Spacer(4) + + TextFlow::Column(cols.right).width(consoleWidth - 7 - optWidth); + os << row << std::endl; + } } - friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { - parser.writeToStream( os ); - return os; + friend auto operator<<(std::ostream &os, Parser const &parser) -> std::ostream & { + parser.writeToStream(os); + return os; } auto validate() const -> Result override { - for( auto const &opt : m_options ) { - auto result = opt.validate(); - if( !result ) - return result; - } - for( auto const &arg : m_args ) { - auto result = arg.validate(); - if( !result ) - return result; - } - return Result::ok(); + for (auto const &opt : m_options) { + auto result = opt.validate(); + if (!result) + return result; + } + for (auto const &arg : m_args) { + auto result = arg.validate(); + if (!result) + return result; + } + return Result::ok(); } using ParserBase::parse; - auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + auto parse(std::string const &exeName, TokenStream const &tokens) const -> InternalParseResult override { + struct ParserInfo { + ParserBase const *parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert(totalParsers < 512); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; - struct ParserInfo { - ParserBase const* parser = nullptr; - size_t count = 0; - }; - const size_t totalParsers = m_options.size() + m_args.size(); - assert( totalParsers < 512 ); - // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do - ParserInfo parseInfos[512]; + { + size_t i = 0; + for (auto const &opt : m_options) + parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) + parseInfos[i++].parser = &arg; + } - { - size_t i = 0; - for (auto const &opt : m_options) parseInfos[i++].parser = &opt; - for (auto const &arg : m_args) parseInfos[i++].parser = &arg; - } + m_exeName.set(exeName); - m_exeName.set( exeName ); + auto result = InternalParseResult::ok(ParseState(ParseResultType::NoMatch, tokens)); + while (result.value().remainingTokens()) { + bool tokenParsed = false; - auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); - while( result.value().remainingTokens() ) { - bool tokenParsed = false; - - for( size_t i = 0; i < totalParsers; ++i ) { - auto& parseInfo = parseInfos[i]; - if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { - result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); - if (!result) - return result; - if (result.value().type() != ParseResultType::NoMatch) { - tokenParsed = true; - ++parseInfo.count; - break; - } - } + for (size_t i = 0; i < totalParsers; ++i) { + auto &parseInfo = parseInfos[i]; + if (parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality()) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; } - - if( result.value().type() == ParseResultType::ShortCircuitAll ) - return result; - if( !tokenParsed ) - return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } } - // !TBD Check missing required options - return result; + + if (result.value().type() == ParseResultType::ShortCircuitAll) + return result; + if (!tokenParsed) + return InternalParseResult::runtimeError("Unrecognised token: " + result.value().remainingTokens()->token); + } + // !TBD Check missing required options + return result; } - }; + }; - template - template - auto ComposableParserImpl::operator|( T const &other ) const -> Parser { - return Parser() | static_cast( *this ) | other; - } -} // namespace detail + template + template + auto ComposableParserImpl::operator|(T const &other) const -> Parser { + return Parser() | static_cast(*this) | other; + } + } // namespace detail -// A Combined parser -using detail::Parser; + // A Combined parser + using detail::Parser; -// A parser for options -using detail::Opt; + // A parser for options + using detail::Opt; -// A parser for arguments -using detail::Arg; + // A parser for arguments + using detail::Arg; -// Wrapper for argc, argv from main() -using detail::Args; + // Wrapper for argc, argv from main() + using detail::Args; -// Specifies the name of the executable -using detail::ExeName; + // Specifies the name of the executable + using detail::ExeName; -// Convenience wrapper for option parser that specifies the help option -using detail::Help; + // Convenience wrapper for option parser that specifies the help option + using detail::Help; -// enum of result types from a parse -using detail::ParseResultType; + // enum of result types from a parse + using detail::ParseResultType; -// Result type for parser operation -using detail::ParserResult; + // Result type for parser operation + using detail::ParserResult; -}} // namespace Catch::clara + } // namespace clara +} // namespace Catch // end clara.hpp #ifdef __clang__ @@ -6466,192 +9965,163 @@ using detail::ParserResult; // end catch_clara.h namespace Catch { - - clara::Parser makeCommandLineParser( ConfigData& config ); + clara::Parser makeCommandLineParser(ConfigData &config); } // end namespace Catch // end catch_commandline.h -#include #include +#include namespace Catch { + clara::Parser makeCommandLineParser(ConfigData &config) { + using namespace clara; - clara::Parser makeCommandLineParser( ConfigData& config ) { + auto const setWarning = [&](std::string const &warning) { + auto warningSet = [&]() { + if (warning == "NoAssertions") + return WarnAbout::NoAssertions; - using namespace clara; + if (warning == "NoTests") + return WarnAbout::NoTests; - auto const setWarning = [&]( std::string const& warning ) { - auto warningSet = [&]() { - if( warning == "NoAssertions" ) - return WarnAbout::NoAssertions; + return WarnAbout::Nothing; + }(); - if ( warning == "NoTests" ) - return WarnAbout::NoTests; + if (warningSet == WarnAbout::Nothing) + return ParserResult::runtimeError("Unrecognised warning: '" + warning + "'"); + config.warnings = static_cast(config.warnings | warningSet); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const loadTestNamesFromFile = [&](std::string const &filename) { + std::ifstream f(filename.c_str()); + if (!f.is_open()) + return ParserResult::runtimeError("Unable to load input file: '" + filename + "'"); - return WarnAbout::Nothing; - }(); + std::string line; + while (std::getline(f, line)) { + line = trim(line); + if (!line.empty() && !startsWith(line, '#')) { + if (!startsWith(line, '"')) + line = '"' + line + '"'; + config.testsOrTags.push_back(line); + config.testsOrTags.emplace_back(","); + } + } + // Remove comma in the end + if (!config.testsOrTags.empty()) + config.testsOrTags.erase(config.testsOrTags.end() - 1); - if (warningSet == WarnAbout::Nothing) - return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" ); - config.warnings = static_cast( config.warnings | warningSet ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const loadTestNamesFromFile = [&]( std::string const& filename ) { - std::ifstream f( filename.c_str() ); - if( !f.is_open() ) - return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" ); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setTestOrder = [&](std::string const &order) { + if (startsWith("declared", order)) + config.runOrder = RunTests::InDeclarationOrder; + else if (startsWith("lexical", order)) + config.runOrder = RunTests::InLexicographicalOrder; + else if (startsWith("random", order)) + config.runOrder = RunTests::InRandomOrder; + else + return clara::ParserResult::runtimeError("Unrecognised ordering: '" + order + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setRngSeed = [&](std::string const &seed) { + if (seed != "time") + return clara::detail::convertInto(seed, config.rngSeed); + config.rngSeed = static_cast(std::time(nullptr)); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setColourUsage = [&](std::string const &useColour) { + auto mode = toLower(useColour); - std::string line; - while( std::getline( f, line ) ) { - line = trim(line); - if( !line.empty() && !startsWith( line, '#' ) ) { - if( !startsWith( line, '"' ) ) - line = '"' + line + '"'; - config.testsOrTags.push_back( line + ',' ); - } - } - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setTestOrder = [&]( std::string const& order ) { - if( startsWith( "declared", order ) ) - config.runOrder = RunTests::InDeclarationOrder; - else if( startsWith( "lexical", order ) ) - config.runOrder = RunTests::InLexicographicalOrder; - else if( startsWith( "random", order ) ) - config.runOrder = RunTests::InRandomOrder; - else - return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setRngSeed = [&]( std::string const& seed ) { - if( seed != "time" ) - return clara::detail::convertInto( seed, config.rngSeed ); - config.rngSeed = static_cast( std::time(nullptr) ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setColourUsage = [&]( std::string const& useColour ) { - auto mode = toLower( useColour ); + if (mode == "yes") + config.useColour = UseColour::Yes; + else if (mode == "no") + config.useColour = UseColour::No; + else if (mode == "auto") + config.useColour = UseColour::Auto; + else + return ParserResult::runtimeError("colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setWaitForKeypress = [&](std::string const &keypress) { + auto keypressLc = toLower(keypress); + if (keypressLc == "never") + config.waitForKeypress = WaitForKeypress::Never; + else if (keypressLc == "start") + config.waitForKeypress = WaitForKeypress::BeforeStart; + else if (keypressLc == "exit") + config.waitForKeypress = WaitForKeypress::BeforeExit; + else if (keypressLc == "both") + config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; + else + return ParserResult::runtimeError("keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setVerbosity = [&](std::string const &verbosity) { + auto lcVerbosity = toLower(verbosity); + if (lcVerbosity == "quiet") + config.verbosity = Verbosity::Quiet; + else if (lcVerbosity == "normal") + config.verbosity = Verbosity::Normal; + else if (lcVerbosity == "high") + config.verbosity = Verbosity::High; + else + return ParserResult::runtimeError("Unrecognised verbosity, '" + verbosity + "'"); + return ParserResult::ok(ParseResultType::Matched); + }; + auto const setReporter = [&](std::string const &reporter) { + IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); - if( mode == "yes" ) - config.useColour = UseColour::Yes; - else if( mode == "no" ) - config.useColour = UseColour::No; - else if( mode == "auto" ) - config.useColour = UseColour::Auto; - else - return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setWaitForKeypress = [&]( std::string const& keypress ) { - auto keypressLc = toLower( keypress ); - if( keypressLc == "start" ) - config.waitForKeypress = WaitForKeypress::BeforeStart; - else if( keypressLc == "exit" ) - config.waitForKeypress = WaitForKeypress::BeforeExit; - else if( keypressLc == "both" ) - config.waitForKeypress = WaitForKeypress::BeforeStartAndExit; - else - return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" ); - return ParserResult::ok( ParseResultType::Matched ); - }; - auto const setVerbosity = [&]( std::string const& verbosity ) { - auto lcVerbosity = toLower( verbosity ); - if( lcVerbosity == "quiet" ) - config.verbosity = Verbosity::Quiet; - else if( lcVerbosity == "normal" ) - config.verbosity = Verbosity::Normal; - else if( lcVerbosity == "high" ) - config.verbosity = Verbosity::High; - else - return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" ); - return ParserResult::ok( ParseResultType::Matched ); - }; + auto lcReporter = toLower(reporter); + auto result = factories.find(lcReporter); - auto cli - = ExeName( config.processName ) - | Help( config.showHelp ) - | Opt( config.listTests ) - ["-l"]["--list-tests"] - ( "list all/matching test cases" ) - | Opt( config.listTags ) - ["-t"]["--list-tags"] - ( "list all/matching tags" ) - | Opt( config.showSuccessfulTests ) - ["-s"]["--success"] - ( "include successful tests in output" ) - | Opt( config.shouldDebugBreak ) - ["-b"]["--break"] - ( "break into debugger on failure" ) - | Opt( config.noThrow ) - ["-e"]["--nothrow"] - ( "skip exception tests" ) - | Opt( config.showInvisibles ) - ["-i"]["--invisibles"] - ( "show invisibles (tabs, newlines)" ) - | Opt( config.outputFilename, "filename" ) - ["-o"]["--out"] - ( "output filename" ) - | Opt( config.reporterNames, "name" ) - ["-r"]["--reporter"] - ( "reporter to use (defaults to console)" ) - | Opt( config.name, "name" ) - ["-n"]["--name"] - ( "suite name" ) - | Opt( [&]( bool ){ config.abortAfter = 1; } ) - ["-a"]["--abort"] - ( "abort at first failure" ) - | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" ) - ["-x"]["--abortx"] - ( "abort after x failures" ) - | Opt( setWarning, "warning name" ) - ["-w"]["--warn"] - ( "enable warnings" ) - | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" ) - ["-d"]["--durations"] - ( "show test durations" ) - | Opt( loadTestNamesFromFile, "filename" ) - ["-f"]["--input-file"] - ( "load test names to run from a file" ) - | Opt( config.filenamesAsTags ) - ["-#"]["--filenames-as-tags"] - ( "adds a tag for the filename" ) - | Opt( config.sectionsToRun, "section name" ) - ["-c"]["--section"] - ( "specify section to run" ) - | Opt( setVerbosity, "quiet|normal|high" ) - ["-v"]["--verbosity"] - ( "set output verbosity" ) - | Opt( config.listTestNamesOnly ) - ["--list-test-names-only"] - ( "list all/matching test cases names only" ) - | Opt( config.listReporters ) - ["--list-reporters"] - ( "list all reporters" ) - | Opt( setTestOrder, "decl|lex|rand" ) - ["--order"] - ( "test case order (defaults to decl)" ) - | Opt( setRngSeed, "'time'|number" ) - ["--rng-seed"] - ( "set a specific seed for random numbers" ) - | Opt( setColourUsage, "yes|no" ) - ["--use-colour"] - ( "should output be colourised" ) - | Opt( config.libIdentify ) - ["--libidentify"] - ( "report name and version according to libidentify standard" ) - | Opt( setWaitForKeypress, "start|exit|both" ) - ["--wait-for-keypress"] - ( "waits for a keypress before exiting" ) - | Opt( config.benchmarkResolutionMultiple, "multiplier" ) - ["--benchmark-resolution-multiple"] - ( "multiple of clock resolution to run benchmarks" ) + if (factories.end() != result) + config.reporterName = lcReporter; + else + return ParserResult::runtimeError("Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters"); + return ParserResult::ok(ParseResultType::Matched); + }; - | Arg( config.testsOrTags, "test name|pattern|tags" ) - ( "which test or tests to use" ); + auto cli = ExeName(config.processName) | Help(config.showHelp) + | Opt(config.listTests)["-l"]["--list-tests"]("list all/matching test cases") + | Opt(config.listTags)["-t"]["--list-tags"]("list all/matching tags") + | Opt(config.showSuccessfulTests)["-s"]["--success"]("include successful tests in output") + | Opt(config.shouldDebugBreak)["-b"]["--break"]("break into debugger on failure") + | Opt(config.noThrow)["-e"]["--nothrow"]("skip exception tests") + | Opt(config.showInvisibles)["-i"]["--invisibles"]("show invisibles (tabs, newlines)") + | Opt(config.outputFilename, "filename")["-o"]["--out"]("output filename") + | Opt(setReporter, "name")["-r"]["--reporter"]("reporter to use (defaults to console)") + | Opt(config.name, "name")["-n"]["--name"]("suite name") + | Opt([&](bool) { config.abortAfter = 1; })["-a"]["--abort"]("abort at first failure") + | Opt([&](int x) { config.abortAfter = x; }, "no. failures")["-x"]["--abortx"]("abort after x failures") + | Opt(setWarning, "warning name")["-w"]["--warn"]("enable warnings") + | Opt([&](bool flag) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, + "yes|no")["-d"]["--durations"]("show test durations") + | Opt(config.minDuration, + "seconds")["-D"]["--min-duration"]("show test durations for tests taking at least the given number of seconds") + | Opt(loadTestNamesFromFile, "filename")["-f"]["--input-file"]("load test names to run from a file") + | Opt(config.filenamesAsTags)["-#"]["--filenames-as-tags"]("adds a tag for the filename") + | Opt(config.sectionsToRun, "section name")["-c"]["--section"]("specify section to run") + | Opt(setVerbosity, "quiet|normal|high")["-v"]["--verbosity"]("set output verbosity") + | Opt(config.listTestNamesOnly)["--list-test-names-only"]("list all/matching test cases names only") + | Opt(config.listReporters)["--list-reporters"]("list all reporters") + | Opt(setTestOrder, "decl|lex|rand")["--order"]("test case order (defaults to decl)") + | Opt(setRngSeed, "'time'|number")["--rng-seed"]("set a specific seed for random numbers") + | Opt(setColourUsage, "yes|no")["--use-colour"]("should output be colourised") + | Opt(config.libIdentify)["--libidentify"]("report name and version according to libidentify standard") + | Opt(setWaitForKeypress, "never|start|exit|both")["--wait-for-keypress"]("waits for a keypress before exiting") + | Opt(config.benchmarkSamples, "samples")["--benchmark-samples"]("number of samples to collect (default: 100)") + | Opt(config.benchmarkResamples, "resamples")["--benchmark-resamples"]("number of resamples for the bootstrap (default: 100000)") + | Opt(config.benchmarkConfidenceInterval, "confidence interval")["--benchmark-confidence-interval"]( + "confidence interval for the bootstrap (between 0 and 1, default: 0.95)") + | Opt(config.benchmarkNoAnalysis)["--benchmark-no-analysis"]("perform only measurements; do not perform any analysis") + | Opt(config.benchmarkWarmupTime, "benchmarkWarmupTime")["--benchmark-warmup-time"]( + "amount of time in milliseconds spent on warming up each test (default: 100)") + | Arg(config.testsOrTags, "test name|pattern|tags")("which test or tests to use"); - return cli; - } + return cli; + } } // end namespace Catch // end catch_commandline.cpp @@ -6661,399 +10131,500 @@ namespace Catch { #include namespace Catch { + bool SourceLineInfo::operator==(SourceLineInfo const &other) const noexcept { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator<(SourceLineInfo const &other) const noexcept { + // We can assume that the same file will usually have the same pointer. + // Thus, if the pointers are the same, there is no point in calling the strcmp + return line < other.line || (line == other.line && file != other.file && (std::strcmp(file, other.file) < 0)); + } - bool SourceLineInfo::empty() const noexcept { - return file[0] == '\0'; - } - bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept { - return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); - } - bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept { - return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); - } - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { + std::ostream &operator<<(std::ostream &os, SourceLineInfo const &info) { #ifndef __GNUG__ - os << info.file << '(' << info.line << ')'; + os << info.file << '(' << info.line << ')'; #else - os << info.file << ':' << info.line; + os << info.file << ':' << info.line; #endif - return os; - } + return os; + } - std::string StreamEndStop::operator+() const { - return std::string(); - } + std::string StreamEndStop::operator+() const { + return std::string(); + } - NonCopyable::NonCopyable() = default; - NonCopyable::~NonCopyable() = default; + NonCopyable::NonCopyable() = default; + NonCopyable::~NonCopyable() = default; -} +} // namespace Catch // end catch_common.cpp // start catch_config.cpp -// start catch_enforce.h - -#include - -#define CATCH_PREPARE_EXCEPTION( type, msg ) \ - type( ( Catch::ReusableStringStream() << msg ).str() ) -#define CATCH_INTERNAL_ERROR( msg ) \ - throw CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg); -#define CATCH_ERROR( msg ) \ - throw CATCH_PREPARE_EXCEPTION( std::domain_error, msg ) -#define CATCH_ENFORCE( condition, msg ) \ - do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false) - -// end catch_enforce.h namespace Catch { + Config::Config(ConfigData const &data) + : m_data(data) + , m_stream(openStream()) { + // We need to trim filter specs to avoid trouble with superfluous + // whitespace (esp. important for bdd macros, as those are manually + // aligned with whitespace). - Config::Config( ConfigData const& data ) - : m_data( data ), - m_stream( openStream() ) - { - TestSpecParser parser(ITagAliasRegistry::get()); - if (data.testsOrTags.empty()) { - parser.parse("~[.]"); // All not hidden tests - } - else { - m_hasTestFilters = true; - for( auto const& testOrTags : data.testsOrTags ) - parser.parse( testOrTags ); - } - m_testSpec = parser.testSpec(); + for (auto &elem : m_data.testsOrTags) { + elem = trim(elem); + } + for (auto &elem : m_data.sectionsToRun) { + elem = trim(elem); } - std::string const& Config::getFilename() const { - return m_data.outputFilename ; + TestSpecParser parser(ITagAliasRegistry::get()); + if (!m_data.testsOrTags.empty()) { + m_hasTestFilters = true; + for (auto const &testOrTags : m_data.testsOrTags) { + parser.parse(testOrTags); + } } + m_testSpec = parser.testSpec(); + } - bool Config::listTests() const { return m_data.listTests; } - bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; } - bool Config::listTags() const { return m_data.listTags; } - bool Config::listReporters() const { return m_data.listReporters; } + std::string const &Config::getFilename() const { + return m_data.outputFilename; + } - std::string Config::getProcessName() const { return m_data.processName; } + bool Config::listTests() const { + return m_data.listTests; + } + bool Config::listTestNamesOnly() const { + return m_data.listTestNamesOnly; + } + bool Config::listTags() const { + return m_data.listTags; + } + bool Config::listReporters() const { + return m_data.listReporters; + } - std::vector const& Config::getReporterNames() const { return m_data.reporterNames; } - std::vector const& Config::getTestsOrTags() const { return m_data.testsOrTags; } - std::vector const& Config::getSectionsToRun() const { return m_data.sectionsToRun; } + std::string Config::getProcessName() const { + return m_data.processName; + } + std::string const &Config::getReporterName() const { + return m_data.reporterName; + } - TestSpec const& Config::testSpec() const { return m_testSpec; } - bool Config::hasTestFilters() const { return m_hasTestFilters; } + std::vector const &Config::getTestsOrTags() const { + return m_data.testsOrTags; + } + std::vector const &Config::getSectionsToRun() const { + return m_data.sectionsToRun; + } - bool Config::showHelp() const { return m_data.showHelp; } + TestSpec const &Config::testSpec() const { + return m_testSpec; + } + bool Config::hasTestFilters() const { + return m_hasTestFilters; + } - // IConfig interface - bool Config::allowThrows() const { return !m_data.noThrow; } - std::ostream& Config::stream() const { return m_stream->stream(); } - std::string Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; } - bool Config::includeSuccessfulResults() const { return m_data.showSuccessfulTests; } - bool Config::warnAboutMissingAssertions() const { return !!(m_data.warnings & WarnAbout::NoAssertions); } - bool Config::warnAboutNoTests() const { return !!(m_data.warnings & WarnAbout::NoTests); } - ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; } - RunTests::InWhatOrder Config::runOrder() const { return m_data.runOrder; } - unsigned int Config::rngSeed() const { return m_data.rngSeed; } - int Config::benchmarkResolutionMultiple() const { return m_data.benchmarkResolutionMultiple; } - UseColour::YesOrNo Config::useColour() const { return m_data.useColour; } - bool Config::shouldDebugBreak() const { return m_data.shouldDebugBreak; } - int Config::abortAfter() const { return m_data.abortAfter; } - bool Config::showInvisibles() const { return m_data.showInvisibles; } - Verbosity Config::verbosity() const { return m_data.verbosity; } + bool Config::showHelp() const { + return m_data.showHelp; + } - IStream const* Config::openStream() { - return Catch::makeStream(m_data.outputFilename); - } + // IConfig interface + bool Config::allowThrows() const { + return !m_data.noThrow; + } + std::ostream &Config::stream() const { + return m_stream->stream(); + } + std::string Config::name() const { + return m_data.name.empty() ? m_data.processName : m_data.name; + } + bool Config::includeSuccessfulResults() const { + return m_data.showSuccessfulTests; + } + bool Config::warnAboutMissingAssertions() const { + return !!(m_data.warnings & WarnAbout::NoAssertions); + } + bool Config::warnAboutNoTests() const { + return !!(m_data.warnings & WarnAbout::NoTests); + } + ShowDurations::OrNot Config::showDurations() const { + return m_data.showDurations; + } + double Config::minDuration() const { + return m_data.minDuration; + } + RunTests::InWhatOrder Config::runOrder() const { + return m_data.runOrder; + } + unsigned int Config::rngSeed() const { + return m_data.rngSeed; + } + UseColour::YesOrNo Config::useColour() const { + return m_data.useColour; + } + bool Config::shouldDebugBreak() const { + return m_data.shouldDebugBreak; + } + int Config::abortAfter() const { + return m_data.abortAfter; + } + bool Config::showInvisibles() const { + return m_data.showInvisibles; + } + Verbosity Config::verbosity() const { + return m_data.verbosity; + } + + bool Config::benchmarkNoAnalysis() const { + return m_data.benchmarkNoAnalysis; + } + int Config::benchmarkSamples() const { + return m_data.benchmarkSamples; + } + double Config::benchmarkConfidenceInterval() const { + return m_data.benchmarkConfidenceInterval; + } + unsigned int Config::benchmarkResamples() const { + return m_data.benchmarkResamples; + } + std::chrono::milliseconds Config::benchmarkWarmupTime() const { + return std::chrono::milliseconds(m_data.benchmarkWarmupTime); + } + + IStream const *Config::openStream() { + return Catch::makeStream(m_data.outputFilename); + } } // end namespace Catch // end catch_config.cpp // start catch_console_colour.cpp #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" #endif // start catch_errno_guard.h namespace Catch { + class ErrnoGuard { + public: + ErrnoGuard(); + ~ErrnoGuard(); - class ErrnoGuard { - public: - ErrnoGuard(); - ~ErrnoGuard(); - private: - int m_oldErrno; - }; + private: + int m_oldErrno; + }; -} +} // namespace Catch // end catch_errno_guard.h +// start catch_windows_h_proxy.h + +#if defined(CATCH_PLATFORM_WINDOWS) + +#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +#define CATCH_DEFINED_NOMINMAX +#define NOMINMAX +#endif +#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +#define CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINED_NOMINMAX +#undef NOMINMAX +#endif +#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif + +#endif // defined(CATCH_PLATFORM_WINDOWS) + +// end catch_windows_h_proxy.h #include namespace Catch { - namespace { - - struct IColourImpl { - virtual ~IColourImpl() = default; - virtual void use( Colour::Code _colourCode ) = 0; - }; - - struct NoColourImpl : IColourImpl { - void use( Colour::Code ) {} - - static IColourImpl* instance() { - static NoColourImpl s_instance; - return &s_instance; - } - }; - - } // anon namespace -} // namespace Catch - -#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) -# ifdef CATCH_PLATFORM_WINDOWS -# define CATCH_CONFIG_COLOUR_WINDOWS -# else -# define CATCH_CONFIG_COLOUR_ANSI -# endif -#endif - -#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// - -namespace Catch { -namespace { - - class Win32ColourImpl : public IColourImpl { - public: - Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) - { - CONSOLE_SCREEN_BUFFER_INFO csbiInfo; - GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); - originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); - originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); - } - - virtual void use( Colour::Code _colourCode ) override { - switch( _colourCode ) { - case Colour::None: return setTextAttribute( originalForegroundAttributes ); - case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::Red: return setTextAttribute( FOREGROUND_RED ); - case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); - case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); - case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); - case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); - case Colour::Grey: return setTextAttribute( 0 ); - - case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); - case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); - case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); - case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); - case Colour::BrightYellow: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN ); - - case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); - - default: - CATCH_ERROR( "Unknown colour requested" ); - } - } - - private: - void setTextAttribute( WORD _textAttribute ) { - SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); - } - HANDLE stdoutHandle; - WORD originalForegroundAttributes; - WORD originalBackgroundAttributes; + namespace { + struct IColourImpl { + virtual ~IColourImpl() = default; + virtual void use(Colour::Code _colourCode) = 0; }; - IColourImpl* platformColourInstance() { - static Win32ColourImpl s_instance; + struct NoColourImpl : IColourImpl { + void use(Colour::Code) override {} - IConfigPtr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = UseColour::Yes; - return colourMode == UseColour::Yes - ? &s_instance - : NoColourImpl::instance(); + static IColourImpl *instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // namespace +} // namespace Catch + +#if !defined(CATCH_CONFIG_COLOUR_NONE) && !defined(CATCH_CONFIG_COLOUR_WINDOWS) && !defined(CATCH_CONFIG_COLOUR_ANSI) +#ifdef CATCH_PLATFORM_WINDOWS +#define CATCH_CONFIG_COLOUR_WINDOWS +#else +#define CATCH_CONFIG_COLOUR_ANSI +#endif +#endif + +#if defined(CATCH_CONFIG_COLOUR_WINDOWS) ///////////////////////////////////////// + +namespace Catch { + namespace { + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() + : stdoutHandle(GetStdHandle(STD_OUTPUT_HANDLE)) { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo(stdoutHandle, &csbiInfo); + originalForegroundAttributes = csbiInfo.wAttributes & ~(BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY); + originalBackgroundAttributes = csbiInfo.wAttributes & ~(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY); + } + + void use(Colour::Code _colourCode) override { + switch (_colourCode) { + case Colour::None: + return setTextAttribute(originalForegroundAttributes); + case Colour::White: + return setTextAttribute(FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::Red: + return setTextAttribute(FOREGROUND_RED); + case Colour::Green: + return setTextAttribute(FOREGROUND_GREEN); + case Colour::Blue: + return setTextAttribute(FOREGROUND_BLUE); + case Colour::Cyan: + return setTextAttribute(FOREGROUND_BLUE | FOREGROUND_GREEN); + case Colour::Yellow: + return setTextAttribute(FOREGROUND_RED | FOREGROUND_GREEN); + case Colour::Grey: + return setTextAttribute(0); + + case Colour::LightGrey: + return setTextAttribute(FOREGROUND_INTENSITY); + case Colour::BrightRed: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED); + case Colour::BrightGreen: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN); + case Colour::BrightWhite: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE); + case Colour::BrightYellow: + return setTextAttribute(FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN); + + case Colour::Bright: + CATCH_INTERNAL_ERROR("not a colour"); + + default: + CATCH_ERROR("Unknown colour requested"); + } + } + + private: + void setTextAttribute(WORD _textAttribute) { SetConsoleTextAttribute(stdoutHandle, _textAttribute | originalBackgroundAttributes); } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl *platformColourInstance() { + static Win32ColourImpl s_instance; + + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = UseColour::Yes; + return colourMode == UseColour::Yes ? &s_instance : NoColourImpl::instance(); } -} // end anon namespace -} // end namespace Catch + } // namespace +} // namespace Catch -#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// +#elif defined(CATCH_CONFIG_COLOUR_ANSI) ////////////////////////////////////// #include namespace Catch { -namespace { - + namespace { // use POSIX/ ANSI console terminal codes // Thanks to Adam Strzelecki for original contribution // (http://github.com/nanoant) // https://github.com/philsquared/Catch/pull/131 class PosixColourImpl : public IColourImpl { public: - virtual void use( Colour::Code _colourCode ) override { - switch( _colourCode ) { - case Colour::None: - case Colour::White: return setColour( "[0m" ); - case Colour::Red: return setColour( "[0;31m" ); - case Colour::Green: return setColour( "[0;32m" ); - case Colour::Blue: return setColour( "[0;34m" ); - case Colour::Cyan: return setColour( "[0;36m" ); - case Colour::Yellow: return setColour( "[0;33m" ); - case Colour::Grey: return setColour( "[1;30m" ); + void use(Colour::Code _colourCode) override { + switch (_colourCode) { + case Colour::None: + case Colour::White: + return setColour("[0m"); + case Colour::Red: + return setColour("[0;31m"); + case Colour::Green: + return setColour("[0;32m"); + case Colour::Blue: + return setColour("[0;34m"); + case Colour::Cyan: + return setColour("[0;36m"); + case Colour::Yellow: + return setColour("[0;33m"); + case Colour::Grey: + return setColour("[1;30m"); - case Colour::LightGrey: return setColour( "[0;37m" ); - case Colour::BrightRed: return setColour( "[1;31m" ); - case Colour::BrightGreen: return setColour( "[1;32m" ); - case Colour::BrightWhite: return setColour( "[1;37m" ); - case Colour::BrightYellow: return setColour( "[1;33m" ); + case Colour::LightGrey: + return setColour("[0;37m"); + case Colour::BrightRed: + return setColour("[1;31m"); + case Colour::BrightGreen: + return setColour("[1;32m"); + case Colour::BrightWhite: + return setColour("[1;37m"); + case Colour::BrightYellow: + return setColour("[1;33m"); - case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" ); - default: CATCH_INTERNAL_ERROR( "Unknown colour requested" ); - } - } - static IColourImpl* instance() { - static PosixColourImpl s_instance; - return &s_instance; + case Colour::Bright: + CATCH_INTERNAL_ERROR("not a colour"); + default: + CATCH_INTERNAL_ERROR("Unknown colour requested"); } + } + static IColourImpl *instance() { + static PosixColourImpl s_instance; + return &s_instance; + } private: - void setColour( const char* _escapeCode ) { - Catch::cout() << '\033' << _escapeCode; - } + void setColour(const char *_escapeCode) { getCurrentContext().getConfig()->stream() << '\033' << _escapeCode; } }; bool useColourOnPlatform() { - return -#ifdef CATCH_PLATFORM_MAC - !isDebuggerActive() && + return +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) + !isDebuggerActive() && #endif #if !(defined(__DJGPP__) && defined(__STRICT_ANSI__)) - isatty(STDOUT_FILENO) + isatty(STDOUT_FILENO) #else - false + false #endif - ; + ; } - IColourImpl* platformColourInstance() { - ErrnoGuard guard; - IConfigPtr config = getCurrentContext().getConfig(); - UseColour::YesOrNo colourMode = config - ? config->useColour() - : UseColour::Auto; - if( colourMode == UseColour::Auto ) - colourMode = useColourOnPlatform() - ? UseColour::Yes - : UseColour::No; - return colourMode == UseColour::Yes - ? PosixColourImpl::instance() - : NoColourImpl::instance(); + IColourImpl *platformColourInstance() { + ErrnoGuard guard; + IConfigPtr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config ? config->useColour() : UseColour::Auto; + if (colourMode == UseColour::Auto) + colourMode = useColourOnPlatform() ? UseColour::Yes : UseColour::No; + return colourMode == UseColour::Yes ? PosixColourImpl::instance() : NoColourImpl::instance(); } -} // end anon namespace -} // end namespace Catch + } // namespace +} // namespace Catch -#else // not Windows or ANSI /////////////////////////////////////////////// +#else // not Windows or ANSI /////////////////////////////////////////////// namespace Catch { - - static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + static IColourImpl *platformColourInstance() { + return NoColourImpl::instance(); + } } // end namespace Catch #endif // Windows/ ANSI/ None namespace Catch { + Colour::Colour(Code _colourCode) { + use(_colourCode); + } + Colour::Colour(Colour &&other) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + } + Colour &Colour::operator=(Colour &&other) noexcept { + m_moved = other.m_moved; + other.m_moved = true; + return *this; + } - Colour::Colour( Code _colourCode ) { use( _colourCode ); } - Colour::Colour( Colour&& rhs ) noexcept { - m_moved = rhs.m_moved; - rhs.m_moved = true; - } - Colour& Colour::operator=( Colour&& rhs ) noexcept { - m_moved = rhs.m_moved; - rhs.m_moved = true; - return *this; - } + Colour::~Colour() { + if (!m_moved) + use(None); + } - Colour::~Colour(){ if( !m_moved ) use( None ); } - - void Colour::use( Code _colourCode ) { - static IColourImpl* impl = platformColourInstance(); - impl->use( _colourCode ); + void Colour::use(Code _colourCode) { + static IColourImpl *impl = platformColourInstance(); + // Strictly speaking, this cannot possibly happen. + // However, under some conditions it does happen (see #1626), + // and this change is small enough that we can let practicality + // triumph over purity in this case. + if (impl != nullptr) { + impl->use(_colourCode); } + } - std::ostream& operator << ( std::ostream& os, Colour const& ) { - return os; - } + std::ostream &operator<<(std::ostream &os, Colour const &) { + return os; + } } // end namespace Catch #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif // end catch_console_colour.cpp // start catch_context.cpp namespace Catch { + class Context : public IMutableContext, NonCopyable { + public: // IContext + IResultCapture *getResultCapture() override { return m_resultCapture; } + IRunner *getRunner() override { return m_runner; } - class Context : public IMutableContext, NonCopyable { + IConfigPtr const &getConfig() const override { return m_config; } - public: // IContext - virtual IResultCapture* getResultCapture() override { - return m_resultCapture; - } - virtual IRunner* getRunner() override { - return m_runner; - } + ~Context() override; - virtual IConfigPtr const& getConfig() const override { - return m_config; - } + public: // IMutableContext + void setResultCapture(IResultCapture *resultCapture) override { m_resultCapture = resultCapture; } + void setRunner(IRunner *runner) override { m_runner = runner; } + void setConfig(IConfigPtr const &config) override { m_config = config; } - virtual ~Context() override; + friend IMutableContext &getCurrentMutableContext(); - public: // IMutableContext - virtual void setResultCapture( IResultCapture* resultCapture ) override { - m_resultCapture = resultCapture; - } - virtual void setRunner( IRunner* runner ) override { - m_runner = runner; - } - virtual void setConfig( IConfigPtr const& config ) override { - m_config = config; - } + private: + IConfigPtr m_config; + IRunner *m_runner = nullptr; + IResultCapture *m_resultCapture = nullptr; + }; - friend IMutableContext& getCurrentMutableContext(); + IMutableContext *IMutableContext::currentContext = nullptr; - private: - IConfigPtr m_config; - IRunner* m_runner = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; + void IMutableContext::createContext() { + currentContext = new Context(); + } - IMutableContext *IMutableContext::currentContext = nullptr; + void cleanUpContext() { + delete IMutableContext::currentContext; + IMutableContext::currentContext = nullptr; + } + IContext::~IContext() = default; + IMutableContext::~IMutableContext() = default; + Context::~Context() = default; - void IMutableContext::createContext() - { - currentContext = new Context(); - } + SimplePcg32 &rng() { + static SimplePcg32 s_rng; + return s_rng; + } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; - } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; -} +} // namespace Catch // end catch_context.cpp // start catch_debug_console.cpp @@ -7062,177 +10633,313 @@ namespace Catch { #include namespace Catch { - void writeToDebugConsole( std::string const& text ); + void writeToDebugConsole(std::string const &text); } // end catch_debug_console.h -#ifdef CATCH_PLATFORM_WINDOWS - - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - ::OutputDebugStringA( text.c_str() ); - } - } - -#else - - namespace Catch { - void writeToDebugConsole( std::string const& text ) { - // !TBD: Need a version for Mac/ XCode and other IDEs - Catch::cout() << text; - } - } - -#endif // Platform -// end catch_debug_console.cpp -// start catch_debugger.cpp - -#ifdef CATCH_PLATFORM_MAC - -# include -# include -# include -# include -# include -# include -# include +#if defined(CATCH_CONFIG_ANDROID_LOGWRITE) +#include namespace Catch { + void writeToDebugConsole(std::string const &text) { + __android_log_write(ANDROID_LOG_DEBUG, "Catch", text.c_str()); + } +} // namespace Catch - // The following function is taken directly from the following technical note: - // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html +#elif defined(CATCH_PLATFORM_WINDOWS) - // Returns true if the current process is being debugged (either - // running under the debugger or has a debugger attached post facto). - bool isDebuggerActive(){ +namespace Catch { + void writeToDebugConsole(std::string const &text) { + ::OutputDebugStringA(text.c_str()); + } +} // namespace Catch - int mib[4]; - struct kinfo_proc info; - std::size_t size; +#else - // Initialize the flags so that, if sysctl fails for some bizarre - // reason, we get a predictable result. +namespace Catch { + void writeToDebugConsole(std::string const &text) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } +} // namespace Catch - info.kp_proc.p_flag = 0; +#endif // Platform \ + // end catch_debug_console.cpp +// start catch_debugger.cpp - // Initialize mib, which tells sysctl the info we want, in this case - // we're looking for information about a specific process ID. +#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE) - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = getpid(); +#include +#include +#include +#include +#include - // Call sysctl. +#ifdef __apple_build_version__ +// These headers will only compile with AppleClang (XCode) +// For other compilers (Clang, GCC, ... ) we need to exclude them +#include +#endif - size = sizeof(info); - if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) { - Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; - return false; - } +namespace Catch { +#ifdef __apple_build_version__ + // The following function is taken directly from the following technical note: + // https://developer.apple.com/library/archive/qa/qa1361/_index.html - // We're being debugged if the P_TRACED flag is set. + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive() { + int mib[4]; + struct kinfo_proc info; + std::size_t size; - return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); - } - } // namespace Catch + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if (sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" + << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ((info.kp_proc.p_flag & P_TRACED) != 0); + } +#else + bool isDebuggerActive() { + // We need to find another way to determine this for non-appleclang compilers on macOS + return false; + } +#endif +} // namespace Catch #elif defined(CATCH_PLATFORM_LINUX) - #include - #include +#include +#include - namespace Catch{ - // The standard POSIX way of detecting a debugger is to attempt to - // ptrace() the process, but this needs to be done from a child and not - // this process itself to still allow attaching to this process later - // if wanted, so is rather heavy. Under Linux we have the PID of the - // "debugger" (which doesn't need to be gdb, of course, it could also - // be strace, for example) in /proc/$PID/status, so just get it from - // there instead. - bool isDebuggerActive(){ - // Libstdc++ has a bug, where std::ifstream sets errno to 0 - // This way our users can properly assert over errno values - ErrnoGuard guard; - std::ifstream in("/proc/self/status"); - for( std::string line; std::getline(in, line); ) { - static const int PREFIX_LEN = 11; - if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { - // We're traced if the PID is not 0 and no other PID starts - // with 0 digit, so it's enough to check for just a single - // character. - return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; - } - } +namespace Catch { + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive() { + // Libstdc++ has a bug, where std::ifstream sets errno to 0 + // This way our users can properly assert over errno values + ErrnoGuard guard; + std::ifstream in("/proc/self/status"); + for (std::string line; std::getline(in, line);) { + static const int PREFIX_LEN = 11; + if (line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } - return false; - } - } // namespace Catch + return false; + } +} // namespace Catch #elif defined(_MSC_VER) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } +} // namespace Catch #elif defined(__MINGW32__) - extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); - namespace Catch { - bool isDebuggerActive() { - return IsDebuggerPresent() != 0; - } - } +extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); +namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } +} // namespace Catch #else - namespace Catch { - bool isDebuggerActive() { return false; } - } +namespace Catch { + bool isDebuggerActive() { + return false; + } +} // namespace Catch #endif // Platform // end catch_debugger.cpp // start catch_decomposer.cpp namespace Catch { + ITransientExpression::~ITransientExpression() = default; - ITransientExpression::~ITransientExpression() = default; - - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { - if( lhs.size() + rhs.size() < 40 && - lhs.find('\n') == std::string::npos && - rhs.find('\n') == std::string::npos ) - os << lhs << " " << op << " " << rhs; - else - os << lhs << "\n" << op << "\n" << rhs; - } -} + void formatReconstructedExpression(std::ostream &os, std::string const &lhs, StringRef op, std::string const &rhs) { + if (lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && rhs.find('\n') == std::string::npos) + os << lhs << " " << op << " " << rhs; + else + os << lhs << "\n" + << op << "\n" + << rhs; + } +} // namespace Catch // end catch_decomposer.cpp +// start catch_enforce.cpp + +#include + +namespace Catch { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER) + [[noreturn]] void throw_exception(std::exception const &e) { + Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n" + << "The message was: " << e.what() << '\n'; + std::terminate(); + } +#endif + + [[noreturn]] void throw_logic_error(std::string const &msg) { + throw_exception(std::logic_error(msg)); + } + + [[noreturn]] void throw_domain_error(std::string const &msg) { + throw_exception(std::domain_error(msg)); + } + + [[noreturn]] void throw_runtime_error(std::string const &msg) { + throw_exception(std::runtime_error(msg)); + } + +} // namespace Catch +// end catch_enforce.cpp +// start catch_enum_values_registry.cpp +// start catch_enum_values_registry.h + +#include +#include + +namespace Catch { + namespace Detail { + std::unique_ptr makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values); + + class EnumValuesRegistry : public IMutableEnumValuesRegistry { + std::vector> m_enumInfos; + + EnumInfo const ®isterEnum(StringRef enumName, StringRef allEnums, std::vector const &values) override; + }; + + std::vector parseEnums(StringRef enums); + + } // namespace Detail +} // namespace Catch + +// end catch_enum_values_registry.h + +#include +#include + +namespace Catch { + IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {} + + namespace Detail { + namespace { + // Extracts the actual name part of an enum instance + // In other words, it returns the Blue part of Bikeshed::Colour::Blue + StringRef extractInstanceName(StringRef enumInstance) { + // Find last occurrence of ":" + size_t name_start = enumInstance.size(); + while (name_start > 0 && enumInstance[name_start - 1] != ':') { + --name_start; + } + return enumInstance.substr(name_start, enumInstance.size() - name_start); + } + } // namespace + + std::vector parseEnums(StringRef enums) { + auto enumValues = splitStringRef(enums, ','); + std::vector parsed; + parsed.reserve(enumValues.size()); + for (auto const &enumValue : enumValues) { + parsed.push_back(trim(extractInstanceName(enumValue))); + } + return parsed; + } + + EnumInfo::~EnumInfo() {} + + StringRef EnumInfo::lookup(int value) const { + for (auto const &valueToName : m_values) { + if (valueToName.first == value) + return valueToName.second; + } + return "{** unexpected enum value **}"_sr; + } + + std::unique_ptr makeEnumInfo(StringRef enumName, StringRef allValueNames, std::vector const &values) { + std::unique_ptr enumInfo(new EnumInfo); + enumInfo->m_name = enumName; + enumInfo->m_values.reserve(values.size()); + + const auto valueNames = Catch::Detail::parseEnums(allValueNames); + assert(valueNames.size() == values.size()); + std::size_t i = 0; + for (auto value : values) + enumInfo->m_values.emplace_back(value, valueNames[i++]); + + return enumInfo; + } + + EnumInfo const &EnumValuesRegistry::registerEnum(StringRef enumName, StringRef allValueNames, std::vector const &values) { + m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values)); + return *m_enumInfos.back(); + } + + } // namespace Detail +} // namespace Catch + +// end catch_enum_values_registry.cpp // start catch_errno_guard.cpp #include namespace Catch { - ErrnoGuard::ErrnoGuard():m_oldErrno(errno){} - ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; } -} + ErrnoGuard::ErrnoGuard() + : m_oldErrno(errno) { + } + ErrnoGuard::~ErrnoGuard() { + errno = m_oldErrno; + } +} // namespace Catch // end catch_errno_guard.cpp // start catch_exception_translator_registry.cpp // start catch_exception_translator_registry.h -#include -#include #include +#include +#include namespace Catch { + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry(); + virtual void registerTranslator(const IExceptionTranslator *translator); + std::string translateActiveException() const override; + std::string tryTranslators() const; - class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { - public: - ~ExceptionTranslatorRegistry(); - virtual void registerTranslator( const IExceptionTranslator* translator ); - virtual std::string translateActiveException() const override; - std::string tryTranslators() const; - - private: - std::vector> m_translators; - }; -} + private: + std::vector> m_translators; + }; +} // namespace Catch // end catch_exception_translator_registry.h #ifdef __OBJC__ @@ -7240,426 +10947,486 @@ namespace Catch { #endif namespace Catch { + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {} - ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { - } + void ExceptionTranslatorRegistry::registerTranslator(const IExceptionTranslator *translator) { + m_translators.push_back(std::unique_ptr(translator)); + } - void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) { - m_translators.push_back( std::unique_ptr( translator ) ); - } - - std::string ExceptionTranslatorRegistry::translateActiveException() const { - try { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + std::string ExceptionTranslatorRegistry::translateActiveException() const { + try { #ifdef __OBJC__ - // In Objective-C try objective-c exceptions first - @try { - return tryTranslators(); - } - @catch (NSException *exception) { - return Catch::Detail::stringify( [exception description] ); - } + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } @catch (NSException *exception) { + return Catch::Detail::stringify([exception description]); + } #else - // Compiling a mixed mode project with MSVC means that CLR - // exceptions will be caught in (...) as well. However, these - // do not fill-in std::current_exception and thus lead to crash - // when attempting rethrow. - // /EHa switch also causes structured exceptions to be caught - // here, but they fill-in current_exception properly, so - // at worst the output should be a little weird, instead of - // causing a crash. - if (std::current_exception() == nullptr) { - return "Non C++ exception. Possibly a CLR exception."; - } - return tryTranslators(); + // Compiling a mixed mode project with MSVC means that CLR + // exceptions will be caught in (...) as well. However, these + // do not fill-in std::current_exception and thus lead to crash + // when attempting rethrow. + // /EHa switch also causes structured exceptions to be caught + // here, but they fill-in current_exception properly, so + // at worst the output should be a little weird, instead of + // causing a crash. + if (std::current_exception() == nullptr) { + return "Non C++ exception. Possibly a CLR exception."; + } + return tryTranslators(); #endif - } - catch( TestFailureException& ) { - std::rethrow_exception(std::current_exception()); - } - catch( std::exception& ex ) { - return ex.what(); - } - catch( std::string& msg ) { - return msg; - } - catch( const char* msg ) { - return msg; - } - catch(...) { - return "Unknown exception"; - } + } catch (TestFailureException &) { + std::rethrow_exception(std::current_exception()); + } catch (std::exception &ex) { + return ex.what(); + } catch (std::string &msg) { + return msg; + } catch (const char *msg) { + return msg; + } catch (...) { + return "Unknown exception"; } + } - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if( m_translators.empty() ) - std::rethrow_exception(std::current_exception()); - else - return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + std::string ExceptionTranslatorRegistry::tryTranslators() const { + if (m_translators.empty()) { + std::rethrow_exception(std::current_exception()); + } else { + return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); } -} + } + +#else // ^^ Exceptions are enabled // Exceptions are disabled vv + std::string ExceptionTranslatorRegistry::translateActiveException() const { + CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } + + std::string ExceptionTranslatorRegistry::tryTranslators() const { + CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); + } +#endif + +} // namespace Catch // end catch_exception_translator_registry.cpp // start catch_fatal_condition.cpp -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif +#include -#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_POSIX_SIGNALS) + +namespace Catch { + // If neither SEH nor signal handling is required, the handler impls + // do not have to do anything, and can be empty. + void FatalConditionHandler::engage_platform() {} + void FatalConditionHandler::disengage_platform() {} + FatalConditionHandler::FatalConditionHandler() = default; + FatalConditionHandler::~FatalConditionHandler() = default; + +} // end namespace Catch + +#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) && defined(CATCH_CONFIG_POSIX_SIGNALS) +#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time" +#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) || defined(CATCH_CONFIG_POSIX_SIGNALS) namespace { - // Report the error condition - void reportFatal( char const * const message ) { - Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); - } -} + //! Signals fatal error message to the run context + void reportFatal(char const *const message) { + Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition(message); + } -#endif // signals/SEH handling + //! Minimal size Catch2 needs for its own fatal error handling. + //! Picked anecdotally, so it might not be sufficient on all + //! platforms, and for all configurations. + constexpr std::size_t minStackSizeForErrors = 32 * 1024; +} // end unnamed namespace -#if defined( CATCH_CONFIG_WINDOWS_SEH ) +#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS + +#if defined(CATCH_CONFIG_WINDOWS_SEH) namespace Catch { - struct SignalDefs { DWORD id; const char* name; }; + struct SignalDefs { + DWORD id; + const char *name; + }; - // There is no 1-1 mapping between signals and windows exceptions. - // Windows can easily distinguish between SO and SigSegV, - // but SigInt, SigTerm, etc are handled differently. - static SignalDefs signalDefs[] = { - { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, - { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, - { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, - { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, - }; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + static SignalDefs signalDefs[] = { + {static_cast(EXCEPTION_ILLEGAL_INSTRUCTION), "SIGILL - Illegal instruction signal"}, + {static_cast(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow"}, + {static_cast(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal"}, + {static_cast(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error"}, + }; - LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { - for (auto const& def : signalDefs) { - if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { - reportFatal(def.name); - } - } - // If its not an exception we care about, pass it along. - // This stops us from eating debugger breaks etc. - return EXCEPTION_CONTINUE_SEARCH; + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (auto const &def : signalDefs) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { + reportFatal(def.name); + } } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } - FatalConditionHandler::FatalConditionHandler() { - isSet = true; - // 32k seems enough for Catch to handle stack overflow, - // but the value was found experimentally, so there is no strong guarantee - guaranteeSize = 32 * 1024; - exceptionHandlerHandle = nullptr; - // Register as first handler in current chain - exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); - // Pass in guarantee size to be filled - SetThreadStackGuarantee(&guaranteeSize); + // Since we do not support multiple instantiations, we put these + // into global variables and rely on cleaning them up in outlined + // constructors/destructors + static PVOID exceptionHandlerHandle = nullptr; + + // For MSVC, we reserve part of the stack memory for handling + // memory overflow structured exception. + FatalConditionHandler::FatalConditionHandler() { + ULONG guaranteeSize = static_cast(minStackSizeForErrors); + if (!SetThreadStackGuarantee(&guaranteeSize)) { + // We do not want to fully error out, because needing + // the stack reserve should be rare enough anyway. + Catch::cerr() << "Failed to reserve piece of stack." + << " Stack overflows will not be reported successfully."; } + } - void FatalConditionHandler::reset() { - if (isSet) { - RemoveVectoredExceptionHandler(exceptionHandlerHandle); - SetThreadStackGuarantee(&guaranteeSize); - exceptionHandlerHandle = nullptr; - isSet = false; - } + // We do not attempt to unset the stack guarantee, because + // Windows does not support lowering the stack size guarantee. + FatalConditionHandler::~FatalConditionHandler() = default; + + void FatalConditionHandler::engage_platform() { + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + if (!exceptionHandlerHandle) { + CATCH_RUNTIME_ERROR("Could not register vectored exception handler"); } + } - FatalConditionHandler::~FatalConditionHandler() { - reset(); + void FatalConditionHandler::disengage_platform() { + if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) { + CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler"); } + exceptionHandlerHandle = nullptr; + } -bool FatalConditionHandler::isSet = false; -ULONG FatalConditionHandler::guaranteeSize = 0; -PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; +} // end namespace Catch -} // namespace Catch +#endif // CATCH_CONFIG_WINDOWS_SEH -#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) +#if defined(CATCH_CONFIG_POSIX_SIGNALS) + +#include namespace Catch { + struct SignalDefs { + int id; + const char *name; + }; - struct SignalDefs { - int id; - const char* name; - }; - static SignalDefs signalDefs[] = { - { SIGINT, "SIGINT - Terminal interrupt signal" }, - { SIGILL, "SIGILL - Illegal instruction signal" }, - { SIGFPE, "SIGFPE - Floating point error signal" }, - { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, - { SIGTERM, "SIGTERM - Termination request signal" }, - { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } - }; + static SignalDefs signalDefs[] = {{SIGINT, "SIGINT - Terminal interrupt signal"}, + {SIGILL, "SIGILL - Illegal instruction signal"}, + {SIGFPE, "SIGFPE - Floating point error signal"}, + {SIGSEGV, "SIGSEGV - Segmentation violation signal"}, + {SIGTERM, "SIGTERM - Termination request signal"}, + {SIGABRT, "SIGABRT - Abort (abnormal termination) signal"}}; - void FatalConditionHandler::handleSignal( int sig ) { - char const * name = ""; - for (auto const& def : signalDefs) { - if (sig == def.id) { - name = def.name; - break; - } - } - reset(); - reportFatal(name); - raise( sig ); +// Older GCCs trigger -Wmissing-field-initializers for T foo = {} +// which is zero initialization, but not explicit. We want to avoid +// that. +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" +#endif + + static char *altStackMem = nullptr; + static std::size_t altStackSize = 0; + static stack_t oldSigStack{}; + static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{}; + + static void restorePreviousSignalHandlers() { + // We set signal handlers back to the previous ones. Hopefully + // nobody overwrote them in the meantime, and doesn't expect + // their signal handlers to live past ours given that they + // installed them after ours.. + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); } + // Return the old stack + sigaltstack(&oldSigStack, nullptr); + } - FatalConditionHandler::FatalConditionHandler() { - isSet = true; - stack_t sigStack; - sigStack.ss_sp = altStackMem; - sigStack.ss_size = SIGSTKSZ; - sigStack.ss_flags = 0; - sigaltstack(&sigStack, &oldSigStack); - struct sigaction sa = { }; - - sa.sa_handler = handleSignal; - sa.sa_flags = SA_ONSTACK; - for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { - sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); - } + static void handleSignal(int sig) { + char const *name = ""; + for (auto const &def : signalDefs) { + if (sig == def.id) { + name = def.name; + break; + } } + // We need to restore previous signal handlers and let them do + // their thing, so that the users can have the debugger break + // when a signal is raised, and so on. + restorePreviousSignalHandlers(); + reportFatal(name); + raise(sig); + } - FatalConditionHandler::~FatalConditionHandler() { - reset(); + FatalConditionHandler::FatalConditionHandler() { + assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists"); + if (altStackSize == 0) { + altStackSize = std::max(static_cast(SIGSTKSZ), minStackSizeForErrors); } + altStackMem = new char[altStackSize](); + } - void FatalConditionHandler::reset() { - if( isSet ) { - // Set signals back to previous values -- hopefully nobody overwrote them in the meantime - for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { - sigaction(signalDefs[i].id, &oldSigActions[i], nullptr); - } - // Return the old stack - sigaltstack(&oldSigStack, nullptr); - isSet = false; - } + FatalConditionHandler::~FatalConditionHandler() { + delete[] altStackMem; + // We signal that another instance can be constructed by zeroing + // out the pointer. + altStackMem = nullptr; + } + + void FatalConditionHandler::engage_platform() { + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = altStackSize; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = {}; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); } - - bool FatalConditionHandler::isSet = false; - struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; - stack_t FatalConditionHandler::oldSigStack = {}; - char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; - -} // namespace Catch - -#else - -namespace Catch { - void FatalConditionHandler::reset() {} -} - -#endif // signals/SEH handling + } #if defined(__GNUC__) -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif -// end catch_fatal_condition.cpp + + void FatalConditionHandler::disengage_platform() { + restorePreviousSignalHandlers(); + } + +} // end namespace Catch + +#endif // CATCH_CONFIG_POSIX_SIGNALS \ + // end catch_fatal_condition.cpp +// start catch_generators.cpp + +#include +#include + +namespace Catch { + IGeneratorTracker::~IGeneratorTracker() {} + + const char *GeneratorException::what() const noexcept { + return m_msg; + } + + namespace Generators { + GeneratorUntypedBase::~GeneratorUntypedBase() {} + + auto acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & { + return getResultCapture().acquireGeneratorTracker(generatorName, lineInfo); + } + + } // namespace Generators +} // namespace Catch +// end catch_generators.cpp // start catch_interfaces_capture.cpp namespace Catch { - IResultCapture::~IResultCapture() = default; + IResultCapture::~IResultCapture() = default; } // end catch_interfaces_capture.cpp // start catch_interfaces_config.cpp namespace Catch { - IConfig::~IConfig() = default; + IConfig::~IConfig() = default; } // end catch_interfaces_config.cpp // start catch_interfaces_exception.cpp namespace Catch { - IExceptionTranslator::~IExceptionTranslator() = default; - IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; -} + IExceptionTranslator::~IExceptionTranslator() = default; + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default; +} // namespace Catch // end catch_interfaces_exception.cpp // start catch_interfaces_registry_hub.cpp namespace Catch { - IRegistryHub::~IRegistryHub() = default; - IMutableRegistryHub::~IMutableRegistryHub() = default; -} + IRegistryHub::~IRegistryHub() = default; + IMutableRegistryHub::~IMutableRegistryHub() = default; +} // namespace Catch // end catch_interfaces_registry_hub.cpp // start catch_interfaces_reporter.cpp -// start catch_reporter_multi.h +// start catch_reporter_listening.h namespace Catch { + class ListeningReporter : public IStreamingReporter { + using Reporters = std::vector; + Reporters m_listeners; + IStreamingReporterPtr m_reporter = nullptr; + ReporterPreferences m_preferences; - class MultipleReporters : public IStreamingReporter { - using Reporters = std::vector; - Reporters m_reporters; + public: + ListeningReporter(); - public: - void add( IStreamingReporterPtr&& reporter ); + void addListener(IStreamingReporterPtr &&listener); + void addReporter(IStreamingReporterPtr &&reporter); - public: // IStreamingReporter + public: // IStreamingReporter + ReporterPreferences getPreferences() const override; - ReporterPreferences getPreferences() const override; + void noMatchingTestCases(std::string const &spec) override; - void noMatchingTestCases( std::string const& spec ) override; + void reportInvalidArguments(std::string const &arg) override; - static std::set getSupportedVerbosities(); + static std::set getSupportedVerbosities(); - void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override; - void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override; +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void benchmarkPreparing(std::string const &name) override; + void benchmarkStarting(BenchmarkInfo const &benchmarkInfo) override; + void benchmarkEnded(BenchmarkStats<> const &benchmarkStats) override; + void benchmarkFailed(std::string const &) override; +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void testRunStarting( TestRunInfo const& testRunInfo ) override; - void testGroupStarting( GroupInfo const& groupInfo ) override; - void testCaseStarting( TestCaseInfo const& testInfo ) override; - void sectionStarting( SectionInfo const& sectionInfo ) override; - void assertionStarting( AssertionInfo const& assertionInfo ) override; + void testRunStarting(TestRunInfo const &testRunInfo) override; + void testGroupStarting(GroupInfo const &groupInfo) override; + void testCaseStarting(TestCaseInfo const &testInfo) override; + void sectionStarting(SectionInfo const §ionInfo) override; + void assertionStarting(AssertionInfo const &assertionInfo) override; - // The return value indicates if the messages buffer should be cleared: - bool assertionEnded( AssertionStats const& assertionStats ) override; - void sectionEnded( SectionStats const& sectionStats ) override; - void testCaseEnded( TestCaseStats const& testCaseStats ) override; - void testGroupEnded( TestGroupStats const& testGroupStats ) override; - void testRunEnded( TestRunStats const& testRunStats ) override; + // The return value indicates if the messages buffer should be cleared: + bool assertionEnded(AssertionStats const &assertionStats) override; + void sectionEnded(SectionStats const §ionStats) override; + void testCaseEnded(TestCaseStats const &testCaseStats) override; + void testGroupEnded(TestGroupStats const &testGroupStats) override; + void testRunEnded(TestRunStats const &testRunStats) override; - void skipTest( TestCaseInfo const& testInfo ) override; - bool isMulti() const override; - - }; + void skipTest(TestCaseInfo const &testInfo) override; + bool isMulti() const override; + }; } // end namespace Catch -// end catch_reporter_multi.h +// end catch_reporter_listening.h namespace Catch { + ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig) + : m_stream(&_fullConfig->stream()) + , m_fullConfig(_fullConfig) { + } - ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig ) - : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + ReporterConfig::ReporterConfig(IConfigPtr const &_fullConfig, std::ostream &_stream) + : m_stream(&_stream) + , m_fullConfig(_fullConfig) { + } - ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream ) - : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + std::ostream &ReporterConfig::stream() const { + return *m_stream; + } + IConfigPtr ReporterConfig::fullConfig() const { + return m_fullConfig; + } - std::ostream& ReporterConfig::stream() const { return *m_stream; } - IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; } + TestRunInfo::TestRunInfo(std::string const &_name) + : name(_name) { + } - TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {} + GroupInfo::GroupInfo(std::string const &_name, std::size_t _groupIndex, std::size_t _groupsCount) + : name(_name) + , groupIndex(_groupIndex) + , groupsCounts(_groupsCount) { + } - GroupInfo::GroupInfo( std::string const& _name, - std::size_t _groupIndex, - std::size_t _groupsCount ) - : name( _name ), - groupIndex( _groupIndex ), - groupsCounts( _groupsCount ) - {} + AssertionStats::AssertionStats(AssertionResult const &_assertionResult, std::vector const &_infoMessages, Totals const &_totals) + : assertionResult(_assertionResult) + , infoMessages(_infoMessages) + , totals(_totals) { + assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - AssertionStats::AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ) - : assertionResult( _assertionResult ), - infoMessages( _infoMessages ), - totals( _totals ) - { - assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; + if (assertionResult.hasMessage()) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder(assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType()); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); - if( assertionResult.hasMessage() ) { - // Copy message into messages list. - // !TBD This should have been done earlier, somewhere - MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); - builder << assertionResult.getMessage(); - builder.m_info.message = builder.m_stream.str(); - - infoMessages.push_back( builder.m_info ); - } + infoMessages.push_back(builder.m_info); } + } - AssertionStats::~AssertionStats() = default; + AssertionStats::~AssertionStats() = default; - SectionStats::SectionStats( SectionInfo const& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ) - : sectionInfo( _sectionInfo ), - assertions( _assertions ), - durationInSeconds( _durationInSeconds ), - missingAssertions( _missingAssertions ) - {} + SectionStats::SectionStats(SectionInfo const &_sectionInfo, Counts const &_assertions, double _durationInSeconds, bool _missingAssertions) + : sectionInfo(_sectionInfo) + , assertions(_assertions) + , durationInSeconds(_durationInSeconds) + , missingAssertions(_missingAssertions) { + } - SectionStats::~SectionStats() = default; + SectionStats::~SectionStats() = default; - TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ) - : testInfo( _testInfo ), - totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), - aborting( _aborting ) - {} + TestCaseStats::TestCaseStats(TestCaseInfo const &_testInfo, Totals const &_totals, std::string const &_stdOut, std::string const &_stdErr, bool _aborting) + : testInfo(_testInfo) + , totals(_totals) + , stdOut(_stdOut) + , stdErr(_stdErr) + , aborting(_aborting) { + } - TestCaseStats::~TestCaseStats() = default; + TestCaseStats::~TestCaseStats() = default; - TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo, - Totals const& _totals, - bool _aborting ) - : groupInfo( _groupInfo ), - totals( _totals ), - aborting( _aborting ) - {} + TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo, Totals const &_totals, bool _aborting) + : groupInfo(_groupInfo) + , totals(_totals) + , aborting(_aborting) { + } - TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo ) - : groupInfo( _groupInfo ), - aborting( false ) - {} + TestGroupStats::TestGroupStats(GroupInfo const &_groupInfo) + : groupInfo(_groupInfo) + , aborting(false) { + } - TestGroupStats::~TestGroupStats() = default; + TestGroupStats::~TestGroupStats() = default; - TestRunStats::TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ) - : runInfo( _runInfo ), - totals( _totals ), - aborting( _aborting ) - {} + TestRunStats::TestRunStats(TestRunInfo const &_runInfo, Totals const &_totals, bool _aborting) + : runInfo(_runInfo) + , totals(_totals) + , aborting(_aborting) { + } - TestRunStats::~TestRunStats() = default; + TestRunStats::~TestRunStats() = default; - void IStreamingReporter::fatalErrorEncountered( StringRef ) {} - bool IStreamingReporter::isMulti() const { return false; } + void IStreamingReporter::fatalErrorEncountered(StringRef) {} + bool IStreamingReporter::isMulti() const { + return false; + } - IReporterFactory::~IReporterFactory() = default; - IReporterRegistry::~IReporterRegistry() = default; - - void addReporter( IStreamingReporterPtr& existingReporter, IStreamingReporterPtr&& additionalReporter ) { - - if( !existingReporter ) { - existingReporter = std::move( additionalReporter ); - return; - } - - MultipleReporters* multi = nullptr; - - if( existingReporter->isMulti() ) { - multi = static_cast( existingReporter.get() ); - } - else { - auto newMulti = std::unique_ptr( new MultipleReporters ); - newMulti->add( std::move( existingReporter ) ); - multi = newMulti.get(); - existingReporter = std::move( newMulti ); - } - multi->add( std::move( additionalReporter ) ); - } + IReporterFactory::~IReporterFactory() = default; + IReporterRegistry::~IReporterRegistry() = default; } // end namespace Catch // end catch_interfaces_reporter.cpp // start catch_interfaces_runner.cpp namespace Catch { - IRunner::~IRunner() = default; + IRunner::~IRunner() = default; } // end catch_interfaces_runner.cpp // start catch_interfaces_testcase.cpp namespace Catch { - ITestInvoker::~ITestInvoker() = default; - ITestCaseRegistry::~ITestCaseRegistry() = default; -} + ITestInvoker::~ITestInvoker() = default; + ITestCaseRegistry::~ITestCaseRegistry() = default; +} // namespace Catch // end catch_interfaces_testcase.cpp // start catch_leak_detector.cpp @@ -7667,24 +11434,28 @@ namespace Catch { #include namespace Catch { - - LeakDetector::LeakDetector() { - int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - flag |= _CRTDBG_LEAK_CHECK_DF; - flag |= _CRTDBG_ALLOC_MEM_DF; - _CrtSetDbgFlag(flag); - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); - _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); - // Change this to leaking allocation's number to break there - _CrtSetBreakAlloc(-1); - } -} + LeakDetector::LeakDetector() { + int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + flag |= _CRTDBG_LEAK_CHECK_DF; + flag |= _CRTDBG_ALLOC_MEM_DF; + _CrtSetDbgFlag(flag); + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG); + _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); + // Change this to leaking allocation's number to break there + _CrtSetBreakAlloc(-1); + } +} // namespace Catch #else - Catch::LeakDetector::LeakDetector() {} +Catch::LeakDetector::LeakDetector() { +} #endif + +Catch::LeakDetector::~LeakDetector() { + Catch::cleanUp(); +} // end catch_leak_detector.cpp // start catch_list.cpp @@ -7693,24 +11464,23 @@ namespace Catch { #include namespace Catch { + std::size_t listTests(Config const &config); - std::size_t listTests( Config const& config ); + std::size_t listTestsNamesOnly(Config const &config); - std::size_t listTestsNamesOnly( Config const& config ); + struct TagInfo { + void add(std::string const &spelling); + std::string all() const; - struct TagInfo { - void add( std::string const& spelling ); - std::string all() const; + std::set spellings; + std::size_t count = 0; + }; - std::set spellings; - std::size_t count = 0; - }; + std::size_t listTags(Config const &config); - std::size_t listTags( Config const& config ); + std::size_t listReporters(); - std::size_t listReporters( Config const& /*config*/ ); - - Option list( Config const& config ); + Option list(std::shared_ptr const &config); } // end namespace Catch @@ -7718,309 +11488,447 @@ namespace Catch { // start catch_text.h namespace Catch { - using namespace clara::TextFlow; + using namespace clara::TextFlow; } // end catch_text.h -#include #include #include +#include namespace Catch { - - std::size_t listTests( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.hasTestFilters() ) - Catch::cout() << "Matching test cases:\n"; - else { - Catch::cout() << "All available test cases:\n"; - } - - auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( auto const& testCaseInfo : matchedTestCases ) { - Colour::Code colour = testCaseInfo.isHidden() - ? Colour::SecondaryText - : Colour::None; - Colour colourGuard( colour ); - - Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n"; - if( config.verbosity() >= Verbosity::High ) { - Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl; - std::string description = testCaseInfo.description; - if( description.empty() ) - description = "(NO DESCRIPTION)"; - Catch::cout() << Column( description ).indent(4) << std::endl; - } - if( !testCaseInfo.tags.empty() ) - Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n"; - } - - if( !config.hasTestFilters() ) - Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl; - else - Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl; - return matchedTestCases.size(); + std::size_t listTests(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; } - std::size_t listTestsNamesOnly( Config const& config ) { - TestSpec testSpec = config.testSpec(); - std::size_t matchedTests = 0; - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( auto const& testCaseInfo : matchedTestCases ) { - matchedTests++; - if( startsWith( testCaseInfo.name, '#' ) ) - Catch::cout() << '"' << testCaseInfo.name << '"'; - else - Catch::cout() << testCaseInfo.name; - if ( config.verbosity() >= Verbosity::High ) - Catch::cout() << "\t@" << testCaseInfo.lineInfo; - Catch::cout() << std::endl; - } - return matchedTests; + auto matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo : matchedTestCases) { + Colour::Code colour = testCaseInfo.isHidden() ? Colour::SecondaryText : Colour::None; + Colour colourGuard(colour); + + Catch::cout() << Column(testCaseInfo.name).initialIndent(2).indent(4) << "\n"; + if (config.verbosity() >= Verbosity::High) { + Catch::cout() << Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl; + std::string description = testCaseInfo.description; + if (description.empty()) + description = "(NO DESCRIPTION)"; + Catch::cout() << Column(description).indent(4) << std::endl; + } + if (!testCaseInfo.tags.empty()) + Catch::cout() << Column(testCaseInfo.tagsAsString()).indent(6) << "\n"; } - void TagInfo::add( std::string const& spelling ) { - ++count; - spellings.insert( spelling ); + if (!config.hasTestFilters()) + Catch::cout() << pluralise(matchedTestCases.size(), "test case") << '\n' + << std::endl; + else + Catch::cout() << pluralise(matchedTestCases.size(), "matching test case") << '\n' + << std::endl; + return matchedTestCases.size(); + } + + std::size_t listTestsNamesOnly(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCaseInfo : matchedTestCases) { + matchedTests++; + if (startsWith(testCaseInfo.name, '#')) + Catch::cout() << '"' << testCaseInfo.name << '"'; + else + Catch::cout() << testCaseInfo.name; + if (config.verbosity() >= Verbosity::High) + Catch::cout() << "\t@" << testCaseInfo.lineInfo; + Catch::cout() << std::endl; + } + return matchedTests; + } + + void TagInfo::add(std::string const &spelling) { + ++count; + spellings.insert(spelling); + } + + std::string TagInfo::all() const { + size_t size = 0; + for (auto const &spelling : spellings) { + // Add 2 for the brackes + size += spelling.size() + 2; } - std::string TagInfo::all() const { - std::string out; - for( auto const& spelling : spellings ) - out += "[" + spelling + "]"; - return out; + std::string out; + out.reserve(size); + for (auto const &spelling : spellings) { + out += '['; + out += spelling; + out += ']'; + } + return out; + } + + std::size_t listTags(Config const &config) { + TestSpec const &testSpec = config.testSpec(); + if (config.hasTestFilters()) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; } - std::size_t listTags( Config const& config ) { - TestSpec testSpec = config.testSpec(); - if( config.hasTestFilters() ) - Catch::cout() << "Tags for matching test cases:\n"; - else { - Catch::cout() << "All available tags:\n"; - } + std::map tagCounts; - std::map tagCounts; - - std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); - for( auto const& testCase : matchedTestCases ) { - for( auto const& tagName : testCase.getTestCaseInfo().tags ) { - std::string lcaseTagName = toLower( tagName ); - auto countIt = tagCounts.find( lcaseTagName ); - if( countIt == tagCounts.end() ) - countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; - countIt->second.add( tagName ); - } - } - - for( auto const& tagCount : tagCounts ) { - ReusableStringStream rss; - rss << " " << std::setw(2) << tagCount.second.count << " "; - auto str = rss.str(); - auto wrapper = Column( tagCount.second.all() ) - .initialIndent( 0 ) - .indent( str.size() ) - .width( CATCH_CONFIG_CONSOLE_WIDTH-10 ); - Catch::cout() << str << wrapper << '\n'; - } - Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; - return tagCounts.size(); + std::vector matchedTestCases = filterTests(getAllTestCasesSorted(config), testSpec, config); + for (auto const &testCase : matchedTestCases) { + for (auto const &tagName : testCase.getTestCaseInfo().tags) { + std::string lcaseTagName = toLower(tagName); + auto countIt = tagCounts.find(lcaseTagName); + if (countIt == tagCounts.end()) + countIt = tagCounts.insert(std::make_pair(lcaseTagName, TagInfo())).first; + countIt->second.add(tagName); + } } - std::size_t listReporters( Config const& /*config*/ ) { - Catch::cout() << "Available reporters:\n"; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); - std::size_t maxNameLen = 0; - for( auto const& factoryKvp : factories ) - maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() ); + for (auto const &tagCount : tagCounts) { + ReusableStringStream rss; + rss << " " << std::setw(2) << tagCount.second.count << " "; + auto str = rss.str(); + auto wrapper = Column(tagCount.second.all()).initialIndent(0).indent(str.size()).width(CATCH_CONFIG_CONSOLE_WIDTH - 10); + Catch::cout() << str << wrapper << '\n'; + } + Catch::cout() << pluralise(tagCounts.size(), "tag") << '\n' + << std::endl; + return tagCounts.size(); + } - for( auto const& factoryKvp : factories ) { - Catch::cout() - << Column( factoryKvp.first + ":" ) - .indent(2) - .width( 5+maxNameLen ) - + Column( factoryKvp.second->getDescription() ) - .initialIndent(0) - .indent(2) - .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) + std::size_t listReporters() { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const &factories = getRegistryHub().getReporterRegistry().getFactories(); + std::size_t maxNameLen = 0; + for (auto const &factoryKvp : factories) + maxNameLen = (std::max)(maxNameLen, factoryKvp.first.size()); + + for (auto const &factoryKvp : factories) { + Catch::cout() << Column(factoryKvp.first + ":").indent(2).width(5 + maxNameLen) + + Column(factoryKvp.second->getDescription()).initialIndent(0).indent(2).width(CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen - 8) << "\n"; - } - Catch::cout() << std::endl; - return factories.size(); } + Catch::cout() << std::endl; + return factories.size(); + } - Option list( Config const& config ) { - Option listedCount; - if( config.listTests() ) - listedCount = listedCount.valueOr(0) + listTests( config ); - if( config.listTestNamesOnly() ) - listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); - if( config.listTags() ) - listedCount = listedCount.valueOr(0) + listTags( config ); - if( config.listReporters() ) - listedCount = listedCount.valueOr(0) + listReporters( config ); - return listedCount; - } + Option list(std::shared_ptr const &config) { + Option listedCount; + getCurrentMutableContext().setConfig(config); + if (config->listTests()) + listedCount = listedCount.valueOr(0) + listTests(*config); + if (config->listTestNamesOnly()) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly(*config); + if (config->listTags()) + listedCount = listedCount.valueOr(0) + listTags(*config); + if (config->listReporters()) + listedCount = listedCount.valueOr(0) + listReporters(); + return listedCount; + } } // end namespace Catch // end catch_list.cpp // start catch_matchers.cpp namespace Catch { -namespace Matchers { + namespace Matchers { namespace Impl { + std::string MatcherUntypedBase::toString() const { + if (m_cachedToString.empty()) + m_cachedToString = describe(); + return m_cachedToString; + } - std::string MatcherUntypedBase::toString() const { - if( m_cachedToString.empty() ) - m_cachedToString = describe(); - return m_cachedToString; - } - - MatcherUntypedBase::~MatcherUntypedBase() = default; + MatcherUntypedBase::~MatcherUntypedBase() = default; } // namespace Impl -} // namespace Matchers + } // namespace Matchers -using namespace Matchers; -using Matchers::Impl::MatcherBase; + using namespace Matchers; + using Matchers::Impl::MatcherBase; } // namespace Catch // end catch_matchers.cpp +// start catch_matchers_exception.cpp + +namespace Catch { + namespace Matchers { + namespace Exception { + bool ExceptionMessageMatcher::match(std::exception const &ex) const { + return ex.what() == m_message; + } + + std::string ExceptionMessageMatcher::describe() const { + return "exception message matches \"" + m_message + "\""; + } + + } // namespace Exception + Exception::ExceptionMessageMatcher Message(std::string const &message) { + return Exception::ExceptionMessageMatcher(message); + } + + // namespace Exception + } // namespace Matchers +} // namespace Catch +// end catch_matchers_exception.cpp // start catch_matchers_floating.cpp -#include -#include -#include -#include +// start catch_polyfills.hpp namespace Catch { -namespace Matchers { -namespace Floating { -enum class FloatingPointKind : uint8_t { - Float, - Double -}; -} -} -} + bool isnan(float f); + bool isnan(double d); +} // namespace Catch -namespace { +// end catch_polyfills.hpp +// start catch_to_string.hpp -template -struct Converter; +#include -template <> -struct Converter { - static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); - Converter(float f) { - std::memcpy(&i, &f, sizeof(f)); +namespace Catch { + template + std::string to_string(T const &t) { +#if defined(CATCH_CONFIG_CPP11_TO_STRING) + return std::to_string(t); +#else + ReusableStringStream rss; + rss << t; + return rss.str(); +#endif + } +} // end namespace Catch + +// end catch_to_string.hpp +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Catch { + namespace { + int32_t convert(float f) { + static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated"); + int32_t i; + std::memcpy(&i, &f, sizeof(f)); + return i; } - int32_t i; -}; -template <> -struct Converter { - static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); - Converter(double d) { - std::memcpy(&i, &d, sizeof(d)); + int64_t convert(double d) { + static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated"); + int64_t i; + std::memcpy(&i, &d, sizeof(d)); + return i; } - int64_t i; -}; -template -auto convert(T t) -> Converter { - return Converter(t); -} - -template -bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) { - // Comparison with NaN should always be false. - // This way we can rule it out before getting into the ugly details - if (std::isnan(lhs) || std::isnan(rhs)) { + template + bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) { + // Comparison with NaN should always be false. + // This way we can rule it out before getting into the ugly details + if (Catch::isnan(lhs) || Catch::isnan(rhs)) { return false; - } + } - auto lc = convert(lhs); - auto rc = convert(rhs); + auto lc = convert(lhs); + auto rc = convert(rhs); - if ((lc.i < 0) != (rc.i < 0)) { + if ((lc < 0) != (rc < 0)) { // Potentially we can have +0 and -0 return lhs == rhs; + } + + // static cast as a workaround for IBM XLC + auto ulpDiff = std::abs(static_cast(lc - rc)); + return static_cast(ulpDiff) <= maxUlpDiff; } - auto ulpDiff = std::abs(lc.i - rc.i); - return ulpDiff <= maxUlpDiff; -} +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -} + float nextafter(float x, float y) { + return ::nextafterf(x, y); + } -namespace Catch { -namespace Matchers { -namespace Floating { - WithinAbsMatcher::WithinAbsMatcher(double target, double margin) - :m_target{ target }, m_margin{ margin } { - if (m_margin < 0) { - throw std::domain_error("Allowed margin difference has to be >= 0"); - } + double nextafter(double x, double y) { + return ::nextafter(x, y); + } + +#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^ + + template + FP step(FP start, FP direction, uint64_t steps) { + for (uint64_t i = 0; i < steps; ++i) { +#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) + start = Catch::nextafter(start, direction); +#else + start = std::nextafter(start, direction); +#endif + } + return start; } // Performs equivalent check of std::fabs(lhs - rhs) <= margin // But without the subtraction to allow for INFINITY in comparison - bool WithinAbsMatcher::match(double const& matchee) const { + bool marginComparison(double lhs, double rhs, double margin) { + return (lhs + margin >= rhs) && (rhs + margin >= lhs); + } + + template + void write(std::ostream &out, FloatingPoint num) { + out << std::scientific << std::setprecision(std::numeric_limits::max_digits10 - 1) << num; + } + + } // end anonymous namespace + + namespace Matchers { + namespace Floating { + enum class FloatingPointKind : uint8_t { + Float, + Double + }; + + WithinAbsMatcher::WithinAbsMatcher(double target, double margin) + : m_target{target} + , m_margin{margin} { + CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.' << " Margin has to be non-negative."); + } + + // Performs equivalent check of std::fabs(lhs - rhs) <= margin + // But without the subtraction to allow for INFINITY in comparison + bool WithinAbsMatcher::match(double const &matchee) const { return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee); - } + } - std::string WithinAbsMatcher::describe() const { + std::string WithinAbsMatcher::describe() const { return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target); - } + } - WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType) - :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } { - if (m_ulps < 0) { - throw std::domain_error("Allowed ulp difference has to be >= 0"); - } - } + WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType) + : m_target{target} + , m_ulps{ulps} + , m_type{baseType} { + CATCH_ENFORCE(m_type == FloatingPointKind::Double || m_ulps < (std::numeric_limits::max)(), + "Provided ULP is impossibly large for a float comparison."); + } - bool WithinUlpsMatcher::match(double const& matchee) const { +#if defined(__clang__) +#pragma clang diagnostic push +// Clang <3.5 reports on the default branch in the switch below +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + bool WithinUlpsMatcher::match(double const &matchee) const { switch (m_type) { - case FloatingPointKind::Float: + case FloatingPointKind::Float: return almostEqualUlps(static_cast(matchee), static_cast(m_target), m_ulps); - case FloatingPointKind::Double: + case FloatingPointKind::Double: return almostEqualUlps(matchee, m_target, m_ulps); - default: - throw std::domain_error("Unknown FloatingPointKind value"); + default: + CATCH_INTERNAL_ERROR("Unknown FloatingPointKind value"); } + } + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + + std::string WithinUlpsMatcher::describe() const { + std::stringstream ret; + + ret << "is within " << m_ulps << " ULPs of "; + + if (m_type == FloatingPointKind::Float) { + write(ret, static_cast(m_target)); + ret << 'f'; + } else { + write(ret, m_target); + } + + ret << " (["; + if (m_type == FloatingPointKind::Double) { + write(ret, step(m_target, static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(m_target, static_cast(INFINITY), m_ulps)); + } else { + // We have to cast INFINITY to float because of MinGW, see #1782 + write(ret, step(static_cast(m_target), static_cast(-INFINITY), m_ulps)); + ret << ", "; + write(ret, step(static_cast(m_target), static_cast(INFINITY), m_ulps)); + } + ret << "])"; + + return ret.str(); + } + + WithinRelMatcher::WithinRelMatcher(double target, double epsilon) + : m_target(target) + , m_epsilon(epsilon) { + CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense."); + CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense."); + } + + bool WithinRelMatcher::match(double const &matchee) const { + const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target)); + return marginComparison(matchee, m_target, std::isinf(relMargin) ? 0 : relMargin); + } + + std::string WithinRelMatcher::describe() const { + Catch::ReusableStringStream sstr; + sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other"; + return sstr.str(); + } + + } // namespace Floating + + Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); } - std::string WithinUlpsMatcher::describe() const { - return "is within " + std::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : ""); + Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) { + return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); } -}// namespace Floating + Floating::WithinAbsMatcher WithinAbs(double target, double margin) { + return Floating::WithinAbsMatcher(target, margin); + } -Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double); -} + Floating::WithinRelMatcher WithinRel(double target, double eps) { + return Floating::WithinRelMatcher(target, eps); + } -Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) { - return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float); -} + Floating::WithinRelMatcher WithinRel(double target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } -Floating::WithinAbsMatcher WithinAbs(double target, double margin) { - return Floating::WithinAbsMatcher(target, margin); -} + Floating::WithinRelMatcher WithinRel(float target, float eps) { + return Floating::WithinRelMatcher(target, eps); + } -} // namespace Matchers + Floating::WithinRelMatcher WithinRel(float target) { + return Floating::WithinRelMatcher(target, std::numeric_limits::epsilon() * 100); + } + + } // namespace Matchers } // namespace Catch - // end catch_matchers_floating.cpp // start catch_matchers_generic.cpp -std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) { - if (desc.empty()) { - return "matches undescribed predicate"; - } else { - return "matches predicate: \"" + desc + '"'; - } +std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string &desc) { + if (desc.empty()) { + return "matches undescribed predicate"; + } else { + return "matches predicate: \"" + desc + '"'; + } } // end catch_matchers_generic.cpp // start catch_matchers_string.cpp @@ -8028,101 +11936,106 @@ std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::str #include namespace Catch { -namespace Matchers { - + namespace Matchers { namespace StdString { + CasedString::CasedString(std::string const &str, CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity) + , m_str(adjustString(str)) { + } + std::string CasedString::adjustString(std::string const &str) const { + return m_caseSensitivity == CaseSensitive::No ? toLower(str) : str; + } + std::string CasedString::caseSensitivitySuffix() const { + return m_caseSensitivity == CaseSensitive::No ? " (case insensitive)" : std::string(); + } - CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_str( adjustString( str ) ) - {} - std::string CasedString::adjustString( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No - ? toLower( str ) - : str; - } - std::string CasedString::caseSensitivitySuffix() const { - return m_caseSensitivity == CaseSensitive::No - ? " (case insensitive)" - : std::string(); + StringMatcherBase::StringMatcherBase(std::string const &operation, CasedString const &comparator) + : m_comparator(comparator) + , m_operation(operation) { + } + + std::string StringMatcherBase::describe() const { + std::string description; + description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + m_comparator.caseSensitivitySuffix().size()); + description += m_operation; + description += ": \""; + description += m_comparator.m_str; + description += "\""; + description += m_comparator.caseSensitivitySuffix(); + return description; + } + + EqualsMatcher::EqualsMatcher(CasedString const &comparator) + : StringMatcherBase("equals", comparator) { + } + + bool EqualsMatcher::match(std::string const &source) const { + return m_comparator.adjustString(source) == m_comparator.m_str; + } + + ContainsMatcher::ContainsMatcher(CasedString const &comparator) + : StringMatcherBase("contains", comparator) { + } + + bool ContainsMatcher::match(std::string const &source) const { + return contains(m_comparator.adjustString(source), m_comparator.m_str); + } + + StartsWithMatcher::StartsWithMatcher(CasedString const &comparator) + : StringMatcherBase("starts with", comparator) { + } + + bool StartsWithMatcher::match(std::string const &source) const { + return startsWith(m_comparator.adjustString(source), m_comparator.m_str); + } + + EndsWithMatcher::EndsWithMatcher(CasedString const &comparator) + : StringMatcherBase("ends with", comparator) { + } + + bool EndsWithMatcher::match(std::string const &source) const { + return endsWith(m_comparator.adjustString(source), m_comparator.m_str); + } + + RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity) + : m_regex(std::move(regex)) + , m_caseSensitivity(caseSensitivity) { + } + + bool RegexMatcher::match(std::string const &matchee) const { + auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway + if (m_caseSensitivity == CaseSensitive::Choice::No) { + flags |= std::regex::icase; } + auto reg = std::regex(m_regex, flags); + return std::regex_match(matchee, reg); + } - StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator ) - : m_comparator( comparator ), - m_operation( operation ) { - } - - std::string StringMatcherBase::describe() const { - std::string description; - description.reserve(5 + m_operation.size() + m_comparator.m_str.size() + - m_comparator.caseSensitivitySuffix().size()); - description += m_operation; - description += ": \""; - description += m_comparator.m_str; - description += "\""; - description += m_comparator.caseSensitivitySuffix(); - return description; - } - - EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {} - - bool EqualsMatcher::match( std::string const& source ) const { - return m_comparator.adjustString( source ) == m_comparator.m_str; - } - - ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {} - - bool ContainsMatcher::match( std::string const& source ) const { - return contains( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {} - - bool StartsWithMatcher::match( std::string const& source ) const { - return startsWith( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {} - - bool EndsWithMatcher::match( std::string const& source ) const { - return endsWith( m_comparator.adjustString( source ), m_comparator.m_str ); - } - - RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {} - - bool RegexMatcher::match(std::string const& matchee) const { - auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway - if (m_caseSensitivity == CaseSensitive::Choice::No) { - flags |= std::regex::icase; - } - auto reg = std::regex(m_regex, flags); - return std::regex_match(matchee, reg); - } - - std::string RegexMatcher::describe() const { - return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively"); - } + std::string RegexMatcher::describe() const { + return "matches " + ::Catch::Detail::stringify(m_regex) + + ((m_caseSensitivity == CaseSensitive::Choice::Yes) ? " case sensitively" : " case insensitively"); + } } // namespace StdString - StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) ); + StdString::EqualsMatcher Equals(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::EqualsMatcher(StdString::CasedString(str, caseSensitivity)); } - StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) ); + StdString::ContainsMatcher Contains(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::ContainsMatcher(StdString::CasedString(str, caseSensitivity)); } - StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + StdString::EndsWithMatcher EndsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::EndsWithMatcher(StdString::CasedString(str, caseSensitivity)); } - StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) { - return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) ); + StdString::StartsWithMatcher StartsWith(std::string const &str, CaseSensitive::Choice caseSensitivity) { + return StdString::StartsWithMatcher(StdString::CasedString(str, caseSensitivity)); } - StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) { - return StdString::RegexMatcher(regex, caseSensitivity); + StdString::RegexMatcher Matches(std::string const ®ex, CaseSensitive::Choice caseSensitivity) { + return StdString::RegexMatcher(regex, caseSensitivity); } -} // namespace Matchers + } // namespace Matchers } // namespace Catch // end catch_matchers_string.cpp // start catch_message.cpp @@ -8130,161 +12043,522 @@ namespace Matchers { // start catch_uncaught_exceptions.h namespace Catch { - bool uncaught_exceptions(); + bool uncaught_exceptions(); } // end namespace Catch // end catch_uncaught_exceptions.h -namespace Catch { - - MessageInfo::MessageInfo( std::string const& _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ) - : macroName( _macroName ), - lineInfo( _lineInfo ), - type( _type ), - sequence( ++globalCount ) - {} - - bool MessageInfo::operator==( MessageInfo const& other ) const { - return sequence == other.sequence; - } - - bool MessageInfo::operator<( MessageInfo const& other ) const { - return sequence < other.sequence; - } - - // This may need protecting if threading support is added - unsigned int MessageInfo::globalCount = 0; - - //////////////////////////////////////////////////////////////////////////// - - Catch::MessageBuilder::MessageBuilder( std::string const& macroName, - SourceLineInfo const& lineInfo, - ResultWas::OfType type ) - :m_info(macroName, lineInfo, type) {} - - //////////////////////////////////////////////////////////////////////////// - - ScopedMessage::ScopedMessage( MessageBuilder const& builder ) - : m_info( builder.m_info ) - { - m_info.message = builder.m_stream.str(); - getResultCapture().pushScopedMessage( m_info ); - } - - ScopedMessage::~ScopedMessage() { - if ( !uncaught_exceptions() ){ - getResultCapture().popScopedMessage(m_info); - } - } -} // end namespace Catch -// end catch_message.cpp -// start catch_random_number_generator.cpp - -// start catch_random_number_generator.h - -#include +#include +#include namespace Catch { + MessageInfo::MessageInfo(StringRef const &_macroName, SourceLineInfo const &_lineInfo, ResultWas::OfType _type) + : macroName(_macroName) + , lineInfo(_lineInfo) + , type(_type) + , sequence(++globalCount) { + } - struct IConfig; + bool MessageInfo::operator==(MessageInfo const &other) const { + return sequence == other.sequence; + } - void seedRng( IConfig const& config ); + bool MessageInfo::operator<(MessageInfo const &other) const { + return sequence < other.sequence; + } - unsigned int rngSeed(); + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; - struct RandomNumberGenerator { - using result_type = unsigned int; + //////////////////////////////////////////////////////////////////////////// - static constexpr result_type (min)() { return 0; } - static constexpr result_type (max)() { return 1000000; } + Catch::MessageBuilder::MessageBuilder(StringRef const ¯oName, SourceLineInfo const &lineInfo, ResultWas::OfType type) + : m_info(macroName, lineInfo, type) { + } - result_type operator()( result_type n ) const; - result_type operator()() const; + //////////////////////////////////////////////////////////////////////////// - template - static void shuffle( V& vector ) { - RandomNumberGenerator rng; - std::shuffle( vector.begin(), vector.end(), rng ); - } + ScopedMessage::ScopedMessage(MessageBuilder const &builder) + : m_info(builder.m_info) + , m_moved() { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage(m_info); + } + + ScopedMessage::ScopedMessage(ScopedMessage &&old) + : m_info(old.m_info) + , m_moved() { + old.m_moved = true; + } + + ScopedMessage::~ScopedMessage() { + if (!uncaught_exceptions() && !m_moved) { + getResultCapture().popScopedMessage(m_info); + } + } + + Capturer::Capturer(StringRef macroName, SourceLineInfo const &lineInfo, ResultWas::OfType resultType, StringRef names) { + auto trimmed = [&](size_t start, size_t end) { + while (names[start] == ',' || isspace(static_cast(names[start]))) { + ++start; + } + while (names[end] == ',' || isspace(static_cast(names[end]))) { + --end; + } + return names.substr(start, end - start + 1); + }; + auto skipq = [&](size_t start, char quote) { + for (auto i = start + 1; i < names.size(); ++i) { + if (names[i] == quote) + return i; + if (names[i] == '\\') + ++i; + } + CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote"); }; -} + size_t start = 0; + std::stack openings; + for (size_t pos = 0; pos < names.size(); ++pos) { + char c = names[pos]; + switch (c) { + case '[': + case '{': + case '(': + // It is basically impossible to disambiguate between + // comparison and start of template args in this context + // case '<': + openings.push(c); + break; + case ']': + case '}': + case ')': + // case '>': + openings.pop(); + break; + case '"': + case '\'': + pos = skipq(pos, c); + break; + case ',': + if (start != pos && openings.empty()) { + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, pos)); + m_messages.back().message += " := "; + start = pos; + } + } + } + assert(openings.empty() && "Mismatched openings"); + m_messages.emplace_back(macroName, lineInfo, resultType); + m_messages.back().message = static_cast(trimmed(start, names.size() - 1)); + m_messages.back().message += " := "; + } + Capturer::~Capturer() { + if (!uncaught_exceptions()) { + assert(m_captured == m_messages.size()); + for (size_t i = 0; i < m_captured; ++i) + m_resultCapture.popScopedMessage(m_messages[i]); + } + } -// end catch_random_number_generator.h -#include + void Capturer::captureValue(size_t index, std::string const &value) { + assert(index < m_messages.size()); + m_messages[index].message += value; + m_resultCapture.pushScopedMessage(m_messages[index]); + m_captured++; + } + +} // end namespace Catch +// end catch_message.cpp +// start catch_output_redirect.cpp + +// start catch_output_redirect.h +#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H +#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H + +#include +#include +#include namespace Catch { + class RedirectedStream { + std::ostream &m_originalStream; + std::ostream &m_redirectionStream; + std::streambuf *m_prevBuf; - void seedRng( IConfig const& config ) { - if( config.rngSeed() != 0 ) - std::srand( config.rngSeed() ); + public: + RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream); + ~RedirectedStream(); + }; + + class RedirectedStdOut { + ReusableStringStream m_rss; + RedirectedStream m_cout; + + public: + RedirectedStdOut(); + auto str() const -> std::string; + }; + + // StdErr has two constituent streams in C++, std::cerr and std::clog + // This means that we need to redirect 2 streams into 1 to keep proper + // order of writes + class RedirectedStdErr { + ReusableStringStream m_rss; + RedirectedStream m_cerr; + RedirectedStream m_clog; + + public: + RedirectedStdErr(); + auto str() const -> std::string; + }; + + class RedirectedStreams { + public: + RedirectedStreams(RedirectedStreams const &) = delete; + RedirectedStreams &operator=(RedirectedStreams const &) = delete; + RedirectedStreams(RedirectedStreams &&) = delete; + RedirectedStreams &operator=(RedirectedStreams &&) = delete; + + RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr); + ~RedirectedStreams(); + + private: + std::string &m_redirectedCout; + std::string &m_redirectedCerr; + RedirectedStdOut m_redirectedStdOut; + RedirectedStdErr m_redirectedStdErr; + }; + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + + // Windows's implementation of std::tmpfile is terrible (it tries + // to create a file inside system folder, thus requiring elevated + // privileges for the binary), so we have to use tmpnam(_s) and + // create the file ourselves there. + class TempFile { + public: + TempFile(TempFile const &) = delete; + TempFile &operator=(TempFile const &) = delete; + TempFile(TempFile &&) = delete; + TempFile &operator=(TempFile &&) = delete; + + TempFile(); + ~TempFile(); + + std::FILE *getFile(); + std::string getContents(); + + private: + std::FILE *m_file = nullptr; +#if defined(_MSC_VER) + char m_buffer[L_tmpnam] = {0}; +#endif + }; + + class OutputRedirect { + public: + OutputRedirect(OutputRedirect const &) = delete; + OutputRedirect &operator=(OutputRedirect const &) = delete; + OutputRedirect(OutputRedirect &&) = delete; + OutputRedirect &operator=(OutputRedirect &&) = delete; + + OutputRedirect(std::string &stdout_dest, std::string &stderr_dest); + ~OutputRedirect(); + + private: + int m_originalStdout = -1; + int m_originalStderr = -1; + TempFile m_stdoutFile; + TempFile m_stderrFile; + std::string &m_stdoutDest; + std::string &m_stderrDest; + }; + +#endif + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H \ + // end catch_output_redirect.h +#include +#include +#include +#include +#include + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#include //_dup and _dup2 +#define dup _dup +#define dup2 _dup2 +#define fileno _fileno +#else +#include // dup and dup2 +#endif +#endif + +namespace Catch { + RedirectedStream::RedirectedStream(std::ostream &originalStream, std::ostream &redirectionStream) + : m_originalStream(originalStream) + , m_redirectionStream(redirectionStream) + , m_prevBuf(m_originalStream.rdbuf()) { + m_originalStream.rdbuf(m_redirectionStream.rdbuf()); + } + + RedirectedStream::~RedirectedStream() { + m_originalStream.rdbuf(m_prevBuf); + } + + RedirectedStdOut::RedirectedStdOut() + : m_cout(Catch::cout(), m_rss.get()) { + } + auto RedirectedStdOut::str() const -> std::string { + return m_rss.str(); + } + + RedirectedStdErr::RedirectedStdErr() + : m_cerr(Catch::cerr(), m_rss.get()) + , m_clog(Catch::clog(), m_rss.get()) { + } + auto RedirectedStdErr::str() const -> std::string { + return m_rss.str(); + } + + RedirectedStreams::RedirectedStreams(std::string &redirectedCout, std::string &redirectedCerr) + : m_redirectedCout(redirectedCout) + , m_redirectedCerr(redirectedCerr) { + } + + RedirectedStreams::~RedirectedStreams() { + m_redirectedCout += m_redirectedStdOut.str(); + m_redirectedCerr += m_redirectedStdErr.str(); + } + +#if defined(CATCH_CONFIG_NEW_CAPTURE) + +#if defined(_MSC_VER) + TempFile::TempFile() { + if (tmpnam_s(m_buffer)) { + CATCH_RUNTIME_ERROR("Could not get a temp filename"); } - unsigned int rngSeed() { - return getCurrentContext().getConfig()->rngSeed(); + if (fopen_s(&m_file, m_buffer, "w+")) { + char buffer[100]; + if (strerror_s(buffer, errno)) { + CATCH_RUNTIME_ERROR("Could not translate errno to a string"); + } + CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer); + } + } +#else + TempFile::TempFile() { + m_file = std::tmpfile(); + if (!m_file) { + CATCH_RUNTIME_ERROR("Could not create a temp file."); + } + } + +#endif + + TempFile::~TempFile() { + // TBD: What to do about errors here? + std::fclose(m_file); +// We manually create the file on Windows only, on Linux +// it will be autodeleted +#if defined(_MSC_VER) + std::remove(m_buffer); +#endif + } + + FILE *TempFile::getFile() { + return m_file; + } + + std::string TempFile::getContents() { + std::stringstream sstr; + char buffer[100] = {}; + std::rewind(m_file); + while (std::fgets(buffer, sizeof(buffer), m_file)) { + sstr << buffer; + } + return sstr.str(); + } + + OutputRedirect::OutputRedirect(std::string &stdout_dest, std::string &stderr_dest) + : m_originalStdout(dup(1)) + , m_originalStderr(dup(2)) + , m_stdoutDest(stdout_dest) + , m_stderrDest(stderr_dest) { + dup2(fileno(m_stdoutFile.getFile()), 1); + dup2(fileno(m_stderrFile.getFile()), 2); + } + + OutputRedirect::~OutputRedirect() { + Catch::cout() << std::flush; + fflush(stdout); + // Since we support overriding these streams, we flush cerr + // even though std::cerr is unbuffered + Catch::cerr() << std::flush; + Catch::clog() << std::flush; + fflush(stderr); + + dup2(m_originalStdout, 1); + dup2(m_originalStderr, 2); + + m_stdoutDest += m_stdoutFile.getContents(); + m_stderrDest += m_stderrFile.getContents(); + } + +#endif // CATCH_CONFIG_NEW_CAPTURE + +} // namespace Catch + +#if defined(CATCH_CONFIG_NEW_CAPTURE) +#if defined(_MSC_VER) +#undef dup +#undef dup2 +#undef fileno +#endif +#endif +// end catch_output_redirect.cpp +// start catch_polyfills.cpp + +#include + +namespace Catch { +#if !defined(CATCH_CONFIG_POLYFILL_ISNAN) + bool isnan(float f) { + return std::isnan(f); + } + bool isnan(double d) { + return std::isnan(d); + } +#else + // For now we only use this for embarcadero + bool isnan(float f) { + return std::_isnan(f); + } + bool isnan(double d) { + return std::_isnan(d); + } +#endif + +} // end namespace Catch +// end catch_polyfills.cpp +// start catch_random_number_generator.cpp + +namespace Catch { + namespace { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4146) // we negate uint32 during the rotate +#endif + // Safe rotr implementation thanks to John Regehr + uint32_t rotate_right(uint32_t val, uint32_t count) { + const uint32_t mask = 31; + count &= mask; + return (val >> count) | (val << (-count & mask)); } - RandomNumberGenerator::result_type RandomNumberGenerator::operator()( result_type n ) const { - return std::rand() % n; - } - RandomNumberGenerator::result_type RandomNumberGenerator::operator()() const { - return std::rand() % (max)(); - } +#if defined(_MSC_VER) +#pragma warning(pop) +#endif -} + } // namespace + + SimplePcg32::SimplePcg32(result_type seed_) { + seed(seed_); + } + + void SimplePcg32::seed(result_type seed_) { + m_state = 0; + (*this)(); + m_state += seed_; + (*this)(); + } + + void SimplePcg32::discard(uint64_t skip) { + // We could implement this to run in O(log n) steps, but this + // should suffice for our use case. + for (uint64_t s = 0; s < skip; ++s) { + static_cast((*this)()); + } + } + + SimplePcg32::result_type SimplePcg32::operator()() { + // prepare the output value + const uint32_t xorshifted = static_cast(((m_state >> 18u) ^ m_state) >> 27u); + const auto output = rotate_right(xorshifted, m_state >> 59u); + + // advance state + m_state = m_state * 6364136223846793005ULL + s_inc; + + return output; + } + + bool operator==(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state == rhs.m_state; + } + + bool operator!=(SimplePcg32 const &lhs, SimplePcg32 const &rhs) { + return lhs.m_state != rhs.m_state; + } +} // namespace Catch // end catch_random_number_generator.cpp // start catch_registry_hub.cpp // start catch_test_case_registry_impl.h -#include -#include #include #include +#include +#include namespace Catch { + class TestCase; + struct IConfig; - class TestCase; - struct IConfig; + std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases); - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + bool isThrowSafe(TestCase const &testCase, IConfig const &config); + bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config); - void enforceNoDuplicateTestCases( std::vector const& functions ); + void enforceNoDuplicateTestCases(std::vector const &functions); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); + std::vector filterTests(std::vector const &testCases, TestSpec const &testSpec, IConfig const &config); + std::vector const &getAllTestCasesSorted(IConfig const &config); - class TestRegistry : public ITestCaseRegistry { - public: - virtual ~TestRegistry() = default; + class TestRegistry : public ITestCaseRegistry { + public: + virtual ~TestRegistry() = default; - virtual void registerTest( TestCase const& testCase ); + virtual void registerTest(TestCase const &testCase); - std::vector const& getAllTests() const override; - std::vector const& getAllTestsSorted( IConfig const& config ) const override; + std::vector const &getAllTests() const override; + std::vector const &getAllTestsSorted(IConfig const &config) const override; - private: - std::vector m_functions; - mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; - mutable std::vector m_sortedFunctions; - std::size_t m_unnamedCount = 0; - std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised - }; + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder; + mutable std::vector m_sortedFunctions; + std::size_t m_unnamedCount = 0; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// - class TestInvokerAsFunction : public ITestInvoker { - void(*m_testAsFunction)(); - public: - TestInvokerAsFunction( void(*testAsFunction)() ) noexcept; + class TestInvokerAsFunction : public ITestInvoker { + void (*m_testAsFunction)(); - void invoke() const override; - }; + public: + TestInvokerAsFunction(void (*testAsFunction)()) noexcept; - std::string extractClassName( StringRef const& classOrQualifiedMethodName ); + void invoke() const override; + }; - /////////////////////////////////////////////////////////////////////////// + std::string extractClassName(StringRef const &classOrQualifiedMethodName); + + /////////////////////////////////////////////////////////////////////////// } // end namespace Catch @@ -8294,26 +12568,23 @@ namespace Catch { #include namespace Catch { + class ReporterRegistry : public IReporterRegistry { + public: + ~ReporterRegistry() override; - class ReporterRegistry : public IReporterRegistry { + IStreamingReporterPtr create(std::string const &name, IConfigPtr const &config) const override; - public: + void registerReporter(std::string const &name, IReporterFactoryPtr const &factory); + void registerListener(IReporterFactoryPtr const &factory); - ~ReporterRegistry() override; + FactoryMap const &getFactories() const override; + Listeners const &getListeners() const override; - IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override; - - void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ); - void registerListener( IReporterFactoryPtr const& factory ); - - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; - - private: - FactoryMap m_factories; - Listeners m_listeners; - }; -} + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} // namespace Catch // end catch_reporter_registry.h // start catch_tag_alias_registry.h @@ -8323,13 +12594,12 @@ namespace Catch { #include namespace Catch { + struct TagAlias { + TagAlias(std::string const &_tag, SourceLineInfo _lineInfo); - struct TagAlias { - TagAlias(std::string const& _tag, SourceLineInfo _lineInfo); - - std::string tag; - SourceLineInfo lineInfo; - }; + std::string tag; + SourceLineInfo lineInfo; + }; } // end namespace Catch @@ -8337,671 +12607,743 @@ namespace Catch { #include namespace Catch { + class TagAliasRegistry : public ITagAliasRegistry { + public: + ~TagAliasRegistry() override; + TagAlias const *find(std::string const &alias) const override; + std::string expandAliases(std::string const &unexpandedTestSpec) const override; + void add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo); - class TagAliasRegistry : public ITagAliasRegistry { - public: - ~TagAliasRegistry() override; - TagAlias const* find( std::string const& alias ) const override; - std::string expandAliases( std::string const& unexpandedTestSpec ) const override; - void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ); - - private: - std::map m_registry; - }; + private: + std::map m_registry; + }; } // end namespace Catch // end catch_tag_alias_registry.h // start catch_startup_exception_registry.h -#include #include +#include namespace Catch { + class StartupExceptionRegistry { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + public: + void add(std::exception_ptr const &exception) noexcept; + std::vector const &getExceptions() const noexcept; - class StartupExceptionRegistry { - public: - void add(std::exception_ptr const& exception) noexcept; - std::vector const& getExceptions() const noexcept; - private: - std::vector m_exceptions; - }; + private: + std::vector m_exceptions; +#endif + }; } // end namespace Catch // end catch_startup_exception_registry.h +// start catch_singletons.hpp + namespace Catch { + struct ISingleton { + virtual ~ISingleton(); + }; - namespace { + void addSingleton(ISingleton *singleton); + void cleanupSingletons(); - class RegistryHub : public IRegistryHub, public IMutableRegistryHub, - private NonCopyable { - - public: // IRegistryHub - RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { - return m_reporterRegistry; - } - ITestCaseRegistry const& getTestCaseRegistry() const override { - return m_testCaseRegistry; - } - IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() override { - return m_exceptionTranslatorRegistry; - } - ITagAliasRegistry const& getTagAliasRegistry() const override { - return m_tagAliasRegistry; - } - StartupExceptionRegistry const& getStartupExceptionRegistry() const override { - return m_exceptionRegistry; - } - - public: // IMutableRegistryHub - void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override { - m_reporterRegistry.registerReporter( name, factory ); - } - void registerListener( IReporterFactoryPtr const& factory ) override { - m_reporterRegistry.registerListener( factory ); - } - void registerTest( TestCase const& testInfo ) override { - m_testCaseRegistry.registerTest( testInfo ); - } - void registerTranslator( const IExceptionTranslator* translator ) override { - m_exceptionTranslatorRegistry.registerTranslator( translator ); - } - void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override { - m_tagAliasRegistry.add( alias, tag, lineInfo ); - } - void registerStartupException() noexcept override { - m_exceptionRegistry.add(std::current_exception()); - } - - private: - TestRegistry m_testCaseRegistry; - ReporterRegistry m_reporterRegistry; - ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; - TagAliasRegistry m_tagAliasRegistry; - StartupExceptionRegistry m_exceptionRegistry; - }; - - // Single, global, instance - RegistryHub*& getTheRegistryHub() { - static RegistryHub* theRegistryHub = nullptr; - if( !theRegistryHub ) - theRegistryHub = new RegistryHub(); - return theRegistryHub; - } + template + class Singleton : SingletonImplT, public ISingleton { + static auto getInternal() -> Singleton * { + static Singleton *s_instance = nullptr; + if (!s_instance) { + s_instance = new Singleton; + addSingleton(s_instance); + } + return s_instance; } - IRegistryHub& getRegistryHub() { - return *getTheRegistryHub(); - } - IMutableRegistryHub& getMutableRegistryHub() { - return *getTheRegistryHub(); - } - void cleanUp() { - delete getTheRegistryHub(); - getTheRegistryHub() = nullptr; - cleanUpContext(); - ReusableStringStream::cleanup(); - } - std::string translateActiveException() { - return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); - } + public: + static auto get() -> InterfaceT const & { return *getInternal(); } + static auto getMutable() -> MutableInterfaceT & { return *getInternal(); } + }; + +} // namespace Catch + +// end catch_singletons.hpp +namespace Catch { + namespace { + class RegistryHub : public IRegistryHub, public IMutableRegistryHub, private NonCopyable { + public: // IRegistryHub + RegistryHub() = default; + IReporterRegistry const &getReporterRegistry() const override { return m_reporterRegistry; } + ITestCaseRegistry const &getTestCaseRegistry() const override { return m_testCaseRegistry; } + IExceptionTranslatorRegistry const &getExceptionTranslatorRegistry() const override { return m_exceptionTranslatorRegistry; } + ITagAliasRegistry const &getTagAliasRegistry() const override { return m_tagAliasRegistry; } + StartupExceptionRegistry const &getStartupExceptionRegistry() const override { return m_exceptionRegistry; } + + public: // IMutableRegistryHub + void registerReporter(std::string const &name, IReporterFactoryPtr const &factory) override { + m_reporterRegistry.registerReporter(name, factory); + } + void registerListener(IReporterFactoryPtr const &factory) override { m_reporterRegistry.registerListener(factory); } + void registerTest(TestCase const &testInfo) override { m_testCaseRegistry.registerTest(testInfo); } + void registerTranslator(const IExceptionTranslator *translator) override { + m_exceptionTranslatorRegistry.registerTranslator(translator); + } + void registerTagAlias(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) override { + m_tagAliasRegistry.add(alias, tag, lineInfo); + } + void registerStartupException() noexcept override { +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + m_exceptionRegistry.add(std::current_exception()); +#else + CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); +#endif + } + IMutableEnumValuesRegistry &getMutableEnumValuesRegistry() override { return m_enumValuesRegistry; } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + TagAliasRegistry m_tagAliasRegistry; + StartupExceptionRegistry m_exceptionRegistry; + Detail::EnumValuesRegistry m_enumValuesRegistry; + }; + } // namespace + + using RegistryHubSingleton = Singleton; + + IRegistryHub const &getRegistryHub() { + return RegistryHubSingleton::get(); + } + IMutableRegistryHub &getMutableRegistryHub() { + return RegistryHubSingleton::getMutable(); + } + void cleanUp() { + cleanupSingletons(); + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } } // end namespace Catch // end catch_registry_hub.cpp // start catch_reporter_registry.cpp namespace Catch { + ReporterRegistry::~ReporterRegistry() = default; - ReporterRegistry::~ReporterRegistry() = default; + IStreamingReporterPtr ReporterRegistry::create(std::string const &name, IConfigPtr const &config) const { + auto it = m_factories.find(name); + if (it == m_factories.end()) + return nullptr; + return it->second->create(ReporterConfig(config)); + } - IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const { - auto it = m_factories.find( name ); - if( it == m_factories.end() ) - return nullptr; - return it->second->create( ReporterConfig( config ) ); - } + void ReporterRegistry::registerReporter(std::string const &name, IReporterFactoryPtr const &factory) { + m_factories.emplace(name, factory); + } + void ReporterRegistry::registerListener(IReporterFactoryPtr const &factory) { + m_listeners.push_back(factory); + } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) { - m_factories.emplace(name, factory); - } - void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) { - m_listeners.push_back( factory ); - } + IReporterRegistry::FactoryMap const &ReporterRegistry::getFactories() const { + return m_factories; + } + IReporterRegistry::Listeners const &ReporterRegistry::getListeners() const { + return m_listeners; + } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; - } - -} +} // namespace Catch // end catch_reporter_registry.cpp // start catch_result_type.cpp namespace Catch { + bool isOk(ResultWas::OfType resultType) { + return (resultType & ResultWas::FailureBit) == 0; + } + bool isJustInfo(int flags) { + return flags == ResultWas::Info; + } - bool isOk( ResultWas::OfType resultType ) { - return ( resultType & ResultWas::FailureBit ) == 0; - } - bool isJustInfo( int flags ) { - return flags == ResultWas::Info; - } + ResultDisposition::Flags operator|(ResultDisposition::Flags lhs, ResultDisposition::Flags rhs) { + return static_cast(static_cast(lhs) | static_cast(rhs)); + } - ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { - return static_cast( static_cast( lhs ) | static_cast( rhs ) ); - } - - bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } - bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + bool shouldContinueOnFailure(int flags) { + return (flags & ResultDisposition::ContinueOnFailure) != 0; + } + bool shouldSuppressFailure(int flags) { + return (flags & ResultDisposition::SuppressFail) != 0; + } } // end namespace Catch // end catch_result_type.cpp // start catch_run_context.cpp -#include #include +#include #include namespace Catch { + namespace Generators { + struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { + GeneratorBasePtr m_generator; - class RedirectedStream { - std::ostream& m_originalStream; - std::ostream& m_redirectionStream; - std::streambuf* m_prevBuf; + GeneratorTracker(TestCaseTracking::NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent) { + } + ~GeneratorTracker(); - public: - RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream ) - : m_originalStream( originalStream ), - m_redirectionStream( redirectionStream ), - m_prevBuf( m_originalStream.rdbuf() ) - { - m_originalStream.rdbuf( m_redirectionStream.rdbuf() ); + static GeneratorTracker &acquire(TrackerContext &ctx, TestCaseTracking::NameAndLocation const &nameAndLocation) { + std::shared_ptr tracker; + + ITracker ¤tTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if (currentTracker.nameAndLocation() == nameAndLocation) { + auto thisTracker = currentTracker.parent().findChild(nameAndLocation); + assert(thisTracker); + assert(thisTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(thisTracker); + } else if (TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isGeneratorTracker()); + tracker = std::static_pointer_cast(childTracker); + } else { + tracker = std::make_shared(nameAndLocation, ctx, ¤tTracker); + currentTracker.addChild(tracker); } - ~RedirectedStream() { - m_originalStream.rdbuf( m_prevBuf ); + + if (!tracker->isComplete()) { + tracker->open(); } + + return *tracker; + } + + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { return !!m_generator; } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if (m_children.empty()) { + return false; + } + // If at least one child started executing, don't wait + if (std::find_if(m_children.begin(), + m_children.end(), + [](TestCaseTracking::ITrackerPtr tracker) { return tracker->hasStarted(); }) + != m_children.end()) { + return false; + } + + // No children have started. We need to check if they _can_ + // start, and thus we should wait for them, or they cannot + // start (due to filters), and we shouldn't wait for them + auto *parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while (!parent->isSectionTracker()) { + parent = &(parent->parent()); + } + assert(parent && "Missing root (test case) level section"); + + auto const &parentSection = static_cast(*parent); + auto const &filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if (filters.empty()) { + return true; + } + + for (auto const &child : m_children) { + if (child->isSectionTracker() + && std::find(filters.begin(), filters.end(), static_cast(*child).trimmedName()) != filters.end()) { + return true; + } + } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + if (should_wait_for_child || (m_runState == CompletedSuccessfully && m_generator->next())) { + m_children.clear(); + m_runState = Executing; + } + } + + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const & override { return m_generator; } + void setGenerator(GeneratorBasePtr &&generator) override { m_generator = std::move(generator); } }; + GeneratorTracker::~GeneratorTracker() {} + } // namespace Generators - class RedirectedStdOut { - ReusableStringStream m_rss; - RedirectedStream m_cout; - public: - RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {} - auto str() const -> std::string { return m_rss.str(); } - }; + RunContext::RunContext(IConfigPtr const &_config, IStreamingReporterPtr &&reporter) + : m_runInfo(_config->name()) + , m_context(getCurrentMutableContext()) + , m_config(_config) + , m_reporter(std::move(reporter)) + , m_lastAssertionInfo{StringRef(), SourceLineInfo("", 0), StringRef(), ResultDisposition::Normal} + , m_includeSuccessfulResults(m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions) { + m_context.setRunner(this); + m_context.setConfig(m_config); + m_context.setResultCapture(this); + m_reporter->testRunStarting(m_runInfo); + } - // StdErr has two constituent streams in C++, std::cerr and std::clog - // This means that we need to redirect 2 streams into 1 to keep proper - // order of writes - class RedirectedStdErr { - ReusableStringStream m_rss; - RedirectedStream m_cerr; - RedirectedStream m_clog; - public: - RedirectedStdErr() - : m_cerr( Catch::cerr(), m_rss.get() ), - m_clog( Catch::clog(), m_rss.get() ) - {} - auto str() const -> std::string { return m_rss.str(); } - }; + RunContext::~RunContext() { + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); + } - RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter) - : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), - m_config(_config), - m_reporter(std::move(reporter)), - m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, - m_includeSuccessfulResults( m_config->includeSuccessfulResults() ) - { - m_context.setRunner(this); - m_context.setConfig(m_config); - m_context.setResultCapture(this); - m_reporter->testRunStarting(m_runInfo); + void RunContext::testGroupStarting(std::string const &testSpec, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); + } + + void RunContext::testGroupEnded(std::string const &testSpec, Totals const &totals, std::size_t groupIndex, std::size_t groupsCount) { + m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); + } + + Totals RunContext::runTest(TestCase const &testCase) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + auto const &testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting(testInfo); + + m_activeTestCase = &testCase; + + ITracker &rootTracker = m_trackerContext.startRun(); + assert(rootTracker.isSectionTracker()); + static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); + runCurrentTest(redirectedCout, redirectedCerr); + } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); + + Totals deltaTotals = m_totals.delta(prevTotals); + if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, redirectedCout, redirectedCerr, aborting())); - RunContext::~RunContext() { - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting())); - } + m_activeTestCase = nullptr; + m_testCaseTracker = nullptr; - void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) { - m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount)); - } + return deltaTotals; + } - void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) { - m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting())); - } + IConfigPtr RunContext::config() const { + return m_config; + } - Totals RunContext::runTest(TestCase const& testCase) { - Totals prevTotals = m_totals; + IStreamingReporter &RunContext::reporter() const { + return *m_reporter; + } - std::string redirectedCout; - std::string redirectedCerr; - - auto const& testInfo = testCase.getTestCaseInfo(); - - m_reporter->testCaseStarting(testInfo); - - m_activeTestCase = &testCase; - - ITracker& rootTracker = m_trackerContext.startRun(); - assert(rootTracker.isSectionTracker()); - static_cast(rootTracker).addInitialFilters(m_config->getSectionsToRun()); - do { - m_trackerContext.startCycle(); - m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo)); - runCurrentTest(redirectedCout, redirectedCerr); - } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting()); - - Totals deltaTotals = m_totals.delta(prevTotals); - if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) { - deltaTotals.assertions.failed++; - deltaTotals.testCases.passed--; - deltaTotals.testCases.failed++; - } - m_totals.testCases += deltaTotals.testCases; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - redirectedCout, - redirectedCerr, - aborting())); - - m_activeTestCase = nullptr; - m_testCaseTracker = nullptr; - - return deltaTotals; - } - - IConfigPtr RunContext::config() const { - return m_config; - } - - IStreamingReporter& RunContext::reporter() const { - return *m_reporter; - } - - void RunContext::assertionEnded(AssertionResult const & result) { - if (result.getResultType() == ResultWas::Ok) { - m_totals.assertions.passed++; - m_lastAssertionPassed = true; - } else if (!result.isOk()) { - m_lastAssertionPassed = false; - if( m_activeTestCase->getTestCaseInfo().okToFail() ) - m_totals.assertions.failedButOk++; - else - m_totals.assertions.failed++; - } - else { - m_lastAssertionPassed = true; - } - - // We have no use for the return value (whether messages should be cleared), because messages were made scoped - // and should be let to clear themselves out. - static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); - - // Reset working state - resetAssertionInfo(); - m_lastResult = result; - } - void RunContext::resetAssertionInfo() { - m_lastAssertionInfo.macroName = StringRef(); - m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; - } - - bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) { - ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); - if (!sectionTracker.isOpen()) - return false; - m_activeSections.push_back(§ionTracker); - - m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; - - m_reporter->sectionStarting(sectionInfo); - - assertions = m_totals.assertions; - - return true; - } - - bool RunContext::testForMissingAssertions(Counts& assertions) { - if (assertions.total() != 0) - return false; - if (!m_config->warnAboutMissingAssertions()) - return false; - if (m_trackerContext.currentTracker().hasChildren()) - return false; + void RunContext::assertionEnded(AssertionResult const &result) { + if (result.getResultType() == ResultWas::Ok) { + m_totals.assertions.passed++; + m_lastAssertionPassed = true; + } else if (!result.isOk()) { + m_lastAssertionPassed = false; + if (m_activeTestCase->getTestCaseInfo().okToFail()) + m_totals.assertions.failedButOk++; + else m_totals.assertions.failed++; - assertions.failed++; - return true; + } else { + m_lastAssertionPassed = true; } - void RunContext::sectionEnded(SectionEndInfo const & endInfo) { - Counts assertions = m_totals.assertions - endInfo.prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); + // We have no use for the return value (whether messages should be cleared), because messages were made scoped + // and should be let to clear themselves out. + static_cast(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals))); - if (!m_activeSections.empty()) { - m_activeSections.back()->close(); - m_activeSections.pop_back(); - } + if (result.getResultType() != ResultWas::Warning) + m_messageScopes.clear(); - m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); - m_messages.clear(); + // Reset working state + resetAssertionInfo(); + m_lastResult = result; + } + void RunContext::resetAssertionInfo() { + m_lastAssertionInfo.macroName = StringRef(); + m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; + } + + bool RunContext::sectionStarted(SectionInfo const §ionInfo, Counts &assertions) { + ITracker §ionTracker + = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo)); + if (!sectionTracker.isOpen()) + return false; + m_activeSections.push_back(§ionTracker); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting(sectionInfo); + + assertions = m_totals.assertions; + + return true; + } + auto RunContext::acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const &lineInfo) -> IGeneratorTracker & { + using namespace Generators; + GeneratorTracker &tracker + = GeneratorTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(static_cast(generatorName), lineInfo)); + m_lastAssertionInfo.lineInfo = lineInfo; + return tracker; + } + + bool RunContext::testForMissingAssertions(Counts &assertions) { + if (assertions.total() != 0) + return false; + if (!m_config->warnAboutMissingAssertions()) + return false; + if (m_trackerContext.currentTracker().hasChildren()) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + void RunContext::sectionEnded(SectionEndInfo const &endInfo) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); + + if (!m_activeSections.empty()) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); } - void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) { - if (m_unfinishedSections.empty()) - m_activeSections.back()->fail(); - else - m_activeSections.back()->close(); - m_activeSections.pop_back(); + m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions)); + m_messages.clear(); + m_messageScopes.clear(); + } - m_unfinishedSections.push_back(endInfo); + void RunContext::sectionEndedEarly(SectionEndInfo const &endInfo) { + if (m_unfinishedSections.empty()) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back(endInfo); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void RunContext::benchmarkPreparing(std::string const &name) { + m_reporter->benchmarkPreparing(name); + } + void RunContext::benchmarkStarting(BenchmarkInfo const &info) { + m_reporter->benchmarkStarting(info); + } + void RunContext::benchmarkEnded(BenchmarkStats<> const &stats) { + m_reporter->benchmarkEnded(stats); + } + void RunContext::benchmarkFailed(std::string const &error) { + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void RunContext::pushScopedMessage(MessageInfo const &message) { + m_messages.push_back(message); + } + + void RunContext::popScopedMessage(MessageInfo const &message) { + m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + } + + void RunContext::emplaceUnscopedMessage(MessageBuilder const &builder) { + m_messageScopes.emplace_back(builder); + } + + std::string RunContext::getCurrentTestName() const { + return m_activeTestCase ? m_activeTestCase->getTestCaseInfo().name : std::string(); + } + + const AssertionResult *RunContext::getLastResult() const { + return &(*m_lastResult); + } + + void RunContext::exceptionEarlyReported() { + m_shouldReportUnexpected = false; + } + + void RunContext::handleFatalErrorCondition(StringRef message) { + // First notify reporter that bad things happened + m_reporter->fatalErrorEncountered(message); + + // Don't rebuild the result -- the stringification itself can cause more fatal errors + // Instead, fake a result data. + AssertionResultData tempResult(ResultWas::FatalErrorCondition, {false}); + tempResult.message = static_cast(message); + AssertionResult result(m_lastAssertionInfo, tempResult); + + assertionEnded(result); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); + m_reporter->sectionEnded(testCaseSectionStats); + + auto const &testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + deltaTotals.assertions.failed = 1; + m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, std::string(), std::string(), false)); + m_totals.testCases.failed++; + testGroupEnded(std::string(), m_totals, 1, 1); + m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); + } + + bool RunContext::lastAssertionPassed() { + return m_lastAssertionPassed; + } + + void RunContext::assertionPassed() { + m_lastAssertionPassed = true; + ++m_totals.assertions.passed; + resetAssertionInfo(); + m_messageScopes.clear(); + } + + bool RunContext::aborting() const { + return m_totals.assertions.failed >= static_cast(m_config->abortAfter()); + } + + void RunContext::runCurrentTest(std::string &redirectedCout, std::string &redirectedCerr) { + auto const &testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name); + m_reporter->sectionStarting(testCaseSection); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + m_shouldReportUnexpected = true; + m_lastAssertionInfo = {"TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal}; + + seedRng(*m_config); + + Timer timer; + CATCH_TRY { + if (m_reporter->getPreferences().shouldRedirectStdOut) { +#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) + RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr); + + timer.start(); + invokeActiveTestCase(); +#else + OutputRedirect r(redirectedCout, redirectedCerr); + timer.start(); + invokeActiveTestCase(); +#endif + } else { + timer.start(); + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); } - void RunContext::benchmarkStarting( BenchmarkInfo const& info ) { - m_reporter->benchmarkStarting( info ); + CATCH_CATCH_ANON(TestFailureException &) { + // This just means the test was aborted due to failure } - void RunContext::benchmarkEnded( BenchmarkStats const& stats ) { - m_reporter->benchmarkEnded( stats ); + CATCH_CATCH_ALL { + // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions + // are reported without translation at the point of origin. + if (m_shouldReportUnexpected) { + AssertionReaction dummyReaction; + handleUnexpectedInflightException(m_lastAssertionInfo, translateActiveException(), dummyReaction); + } } + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions(assertions); - void RunContext::pushScopedMessage(MessageInfo const & message) { - m_messages.push_back(message); + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + m_messageScopes.clear(); + + SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); + m_reporter->sectionEnded(testCaseSectionStats); + } + + void RunContext::invokeActiveTestCase() { + FatalConditionHandlerGuard _(&m_fatalConditionhandler); + m_activeTestCase->invoke(); + } + + void RunContext::handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for (auto it = m_unfinishedSections.rbegin(), itEnd = m_unfinishedSections.rend(); it != itEnd; ++it) + sectionEnded(*it); + m_unfinishedSections.clear(); + } + + void RunContext::handleExpr(AssertionInfo const &info, ITransientExpression const &expr, AssertionReaction &reaction) { + m_reporter->assertionStarting(info); + + bool negated = isFalseTest(info.resultDisposition); + bool result = expr.getResult() != negated; + + if (result) { + if (!m_includeSuccessfulResults) { + assertionPassed(); + } else { + reportExpr(info, ResultWas::Ok, &expr, negated); + } + } else { + reportExpr(info, ResultWas::ExpressionFailed, &expr, negated); + populateReaction(reaction); } + } + void RunContext::reportExpr(AssertionInfo const &info, ResultWas::OfType resultType, ITransientExpression const *expr, bool negated) { + m_lastAssertionInfo = info; + AssertionResultData data(resultType, LazyExpression(negated)); - void RunContext::popScopedMessage(MessageInfo const & message) { - m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end()); + AssertionResult assertionResult{info, data}; + assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; + + assertionEnded(assertionResult); + } + + void RunContext::handleMessage(AssertionInfo const &info, ResultWas::OfType resultType, StringRef const &message, AssertionReaction &reaction) { + m_reporter->assertionStarting(info); + + m_lastAssertionInfo = info; + + AssertionResultData data(resultType, LazyExpression(false)); + data.message = static_cast(message); + AssertionResult assertionResult{m_lastAssertionInfo, data}; + assertionEnded(assertionResult); + if (!assertionResult.isOk()) + populateReaction(reaction); + } + void RunContext::handleUnexpectedExceptionNotThrown(AssertionInfo const &info, AssertionReaction &reaction) { + handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); + } + + void RunContext::handleUnexpectedInflightException(AssertionInfo const &info, std::string const &message, AssertionReaction &reaction) { + m_lastAssertionInfo = info; + + AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); + data.message = message; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + populateReaction(reaction); + } + + void RunContext::populateReaction(AssertionReaction &reaction) { + reaction.shouldDebugBreak = m_config->shouldDebugBreak(); + reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); + } + + void RunContext::handleIncomplete(AssertionInfo const &info) { + m_lastAssertionInfo = info; + + AssertionResultData data(ResultWas::ThrewException, LazyExpression(false)); + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + } + void RunContext::handleNonExpr(AssertionInfo const &info, ResultWas::OfType resultType, AssertionReaction &reaction) { + m_lastAssertionInfo = info; + + AssertionResultData data(resultType, LazyExpression(false)); + AssertionResult assertionResult{info, data}; + assertionEnded(assertionResult); + + if (!assertionResult.isOk()) + populateReaction(reaction); + } + + IResultCapture &getResultCapture() { + if (auto *capture = getCurrentContext().getResultCapture()) + return *capture; + else + CATCH_INTERNAL_ERROR("No result capture instance"); + } + + void seedRng(IConfig const &config) { + if (config.rngSeed() != 0) { + std::srand(config.rngSeed()); + rng().seed(config.rngSeed()); } + } - std::string RunContext::getCurrentTestName() const { - return m_activeTestCase - ? m_activeTestCase->getTestCaseInfo().name - : std::string(); - } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } - const AssertionResult * RunContext::getLastResult() const { - return &(*m_lastResult); - } - - void RunContext::exceptionEarlyReported() { - m_shouldReportUnexpected = false; - } - - void RunContext::handleFatalErrorCondition( StringRef message ) { - // First notify reporter that bad things happened - m_reporter->fatalErrorEncountered(message); - - // Don't rebuild the result -- the stringification itself can cause more fatal errors - // Instead, fake a result data. - AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } ); - tempResult.message = message; - AssertionResult result(m_lastAssertionInfo, tempResult); - - assertionEnded(result); - - handleUnfinishedSections(); - - // Recreate section for test case (as we will lose the one that was in scope) - auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); - - Counts assertions; - assertions.failed = 1; - SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false); - m_reporter->sectionEnded(testCaseSectionStats); - - auto const& testInfo = m_activeTestCase->getTestCaseInfo(); - - Totals deltaTotals; - deltaTotals.testCases.failed = 1; - deltaTotals.assertions.failed = 1; - m_reporter->testCaseEnded(TestCaseStats(testInfo, - deltaTotals, - std::string(), - std::string(), - false)); - m_totals.testCases.failed++; - testGroupEnded(std::string(), m_totals, 1, 1); - m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false)); - } - - bool RunContext::lastAssertionPassed() { - return m_lastAssertionPassed; - } - - void RunContext::assertionPassed() { - m_lastAssertionPassed = true; - ++m_totals.assertions.passed; - resetAssertionInfo(); - } - - bool RunContext::aborting() const { - return m_totals.assertions.failed == static_cast(m_config->abortAfter()); - } - - void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) { - auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); - SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description); - m_reporter->sectionStarting(testCaseSection); - Counts prevAssertions = m_totals.assertions; - double duration = 0; - m_shouldReportUnexpected = true; - m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal }; - - seedRng(*m_config); - - Timer timer; - try { - if (m_reporter->getPreferences().shouldRedirectStdOut) { - RedirectedStdOut redirectedStdOut; - RedirectedStdErr redirectedStdErr; - timer.start(); - invokeActiveTestCase(); - redirectedCout += redirectedStdOut.str(); - redirectedCerr += redirectedStdErr.str(); - - } else { - timer.start(); - invokeActiveTestCase(); - } - duration = timer.getElapsedSeconds(); - } catch (TestFailureException&) { - // This just means the test was aborted due to failure - } catch (...) { - // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions - // are reported without translation at the point of origin. - if( m_shouldReportUnexpected ) { - AssertionReaction dummyReaction; - handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction ); - } - } - Counts assertions = m_totals.assertions - prevAssertions; - bool missingAssertions = testForMissingAssertions(assertions); - - m_testCaseTracker->close(); - handleUnfinishedSections(); - m_messages.clear(); - - SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions); - m_reporter->sectionEnded(testCaseSectionStats); - } - - void RunContext::invokeActiveTestCase() { - FatalConditionHandler fatalConditionHandler; // Handle signals - m_activeTestCase->invoke(); - fatalConditionHandler.reset(); - } - - void RunContext::handleUnfinishedSections() { - // If sections ended prematurely due to an exception we stored their - // infos here so we can tear them down outside the unwind process. - for (auto it = m_unfinishedSections.rbegin(), - itEnd = m_unfinishedSections.rend(); - it != itEnd; - ++it) - sectionEnded(*it); - m_unfinishedSections.clear(); - } - - void RunContext::handleExpr( - AssertionInfo const& info, - ITransientExpression const& expr, - AssertionReaction& reaction - ) { - m_reporter->assertionStarting( info ); - - bool negated = isFalseTest( info.resultDisposition ); - bool result = expr.getResult() != negated; - - if( result ) { - if (!m_includeSuccessfulResults) { - assertionPassed(); - } - else { - reportExpr(info, ResultWas::Ok, &expr, negated); - } - } - else { - reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); - populateReaction( reaction ); - } - } - void RunContext::reportExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - ITransientExpression const *expr, - bool negated ) { - - m_lastAssertionInfo = info; - AssertionResultData data( resultType, LazyExpression( negated ) ); - - AssertionResult assertionResult{ info, data }; - assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - - assertionEnded( assertionResult ); - } - - void RunContext::handleMessage( - AssertionInfo const& info, - ResultWas::OfType resultType, - StringRef const& message, - AssertionReaction& reaction - ) { - m_reporter->assertionStarting( info ); - - m_lastAssertionInfo = info; - - AssertionResultData data( resultType, LazyExpression( false ) ); - data.message = message; - AssertionResult assertionResult{ m_lastAssertionInfo, data }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); - } - void RunContext::handleUnexpectedExceptionNotThrown( - AssertionInfo const& info, - AssertionReaction& reaction - ) { - handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction); - } - - void RunContext::handleUnexpectedInflightException( - AssertionInfo const& info, - std::string const& message, - AssertionReaction& reaction - ) { - m_lastAssertionInfo = info; - - AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; - AssertionResult assertionResult{ info, data }; - assertionEnded( assertionResult ); - populateReaction( reaction ); - } - - void RunContext::populateReaction( AssertionReaction& reaction ) { - reaction.shouldDebugBreak = m_config->shouldDebugBreak(); - reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal); - } - - void RunContext::handleIncomplete( - AssertionInfo const& info - ) { - m_lastAssertionInfo = info; - - AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; - AssertionResult assertionResult{ info, data }; - assertionEnded( assertionResult ); - } - void RunContext::handleNonExpr( - AssertionInfo const &info, - ResultWas::OfType resultType, - AssertionReaction &reaction - ) { - m_lastAssertionInfo = info; - - AssertionResultData data( resultType, LazyExpression( false ) ); - AssertionResult assertionResult{ info, data }; - assertionEnded( assertionResult ); - - if( !assertionResult.isOk() ) - populateReaction( reaction ); - } - - IResultCapture& getResultCapture() { - if (auto* capture = getCurrentContext().getResultCapture()) - return *capture; - else - CATCH_INTERNAL_ERROR("No result capture instance"); - } -} +} // namespace Catch // end catch_run_context.cpp // start catch_section.cpp namespace Catch { + Section::Section(SectionInfo const &info) + : m_info(info) + , m_sectionIncluded(getResultCapture().sectionStarted(m_info, m_assertions)) { + m_timer.start(); + } - Section::Section( SectionInfo const& info ) - : m_info( info ), - m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) - { - m_timer.start(); + Section::~Section() { + if (m_sectionIncluded) { + SectionEndInfo endInfo{m_info, m_assertions, m_timer.getElapsedSeconds()}; + if (uncaught_exceptions()) + getResultCapture().sectionEndedEarly(endInfo); + else + getResultCapture().sectionEnded(endInfo); } + } - Section::~Section() { - if( m_sectionIncluded ) { - SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); - if( uncaught_exceptions() ) - getResultCapture().sectionEndedEarly( endInfo ); - else - getResultCapture().sectionEnded( endInfo ); - } - } - - // This indicates whether the section should be executed or not - Section::operator bool() const { - return m_sectionIncluded; - } + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } } // end namespace Catch // end catch_section.cpp // start catch_section_info.cpp namespace Catch { - - SectionInfo::SectionInfo - ( SourceLineInfo const& _lineInfo, - std::string const& _name, - std::string const& _description ) - : name( _name ), - description( _description ), - lineInfo( _lineInfo ) - {} - - SectionEndInfo::SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) - : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) - {} + SectionInfo::SectionInfo(SourceLineInfo const &_lineInfo, std::string const &_name) + : name(_name) + , lineInfo(_lineInfo) { + } } // end namespace Catch // end catch_section_info.cpp @@ -9012,38 +13354,46 @@ namespace Catch { #include namespace Catch { + class Session : NonCopyable { + public: + Session(); + ~Session() override; - class Session : NonCopyable { - public: + void showHelp() const; + void libIdentify(); - Session(); - ~Session() override; + int applyCommandLine(int argc, char const *const *argv); +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int applyCommandLine(int argc, wchar_t const *const *argv); +#endif - void showHelp() const; - void libIdentify(); + void useConfigData(ConfigData const &configData); - int applyCommandLine( int argc, char const * const * argv ); + template + int run(int argc, CharT const *const argv[]) { + if (m_startupExceptions) + return 1; + int returnCode = applyCommandLine(argc, argv); + if (returnCode == 0) + returnCode = run(); + return returnCode; + } - void useConfigData( ConfigData const& configData ); + int run(); - int run( int argc, char* argv[] ); - #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) - int run( int argc, wchar_t* const argv[] ); - #endif - int run(); + clara::Parser const &cli() const; + void cli(clara::Parser const &newParser); + ConfigData &configData(); + Config &config(); - clara::Parser const& cli() const; - void cli( clara::Parser const& newParser ); - ConfigData& configData(); - Config& config(); - private: - int runInternal(); + private: + int runInternal(); - clara::Parser m_cli; - ConfigData m_configData; - std::shared_ptr m_config; - bool m_startupExceptions = false; - }; + clara::Parser m_cli; + ConfigData m_configData; + std::shared_ptr m_config; + bool m_startupExceptions = false; + }; } // end namespace Catch @@ -9053,1467 +13403,1641 @@ namespace Catch { #include namespace Catch { + // Versioning information + struct Version { + Version(Version const &) = delete; + Version &operator=(Version const &) = delete; + Version(unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const *const _branchName, unsigned int _buildNumber); - // Versioning information - struct Version { - Version( Version const& ) = delete; - Version& operator=( Version const& ) = delete; - Version( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ); + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; - unsigned int const majorVersion; - unsigned int const minorVersion; - unsigned int const patchNumber; + // buildNumber is only used if branchName is not null + char const *const branchName; + unsigned int const buildNumber; - // buildNumber is only used if branchName is not null - char const * const branchName; - unsigned int const buildNumber; + friend std::ostream &operator<<(std::ostream &os, Version const &version); + }; - friend std::ostream& operator << ( std::ostream& os, Version const& version ); - }; - - Version const& libraryVersion(); -} + Version const &libraryVersion(); +} // namespace Catch // end catch_version.h #include #include +#include +#include namespace Catch { + namespace { + const int MaxExitCode = 255; - namespace { - const int MaxExitCode = 255; + IStreamingReporterPtr createReporter(std::string const &reporterName, IConfigPtr const &config) { + auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); + CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); - IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) { - auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config); - CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'"); + return reporter; + } - return reporter; + IStreamingReporterPtr makeReporter(std::shared_ptr const &config) { + if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) { + return createReporter(config->getReporterName(), config); + } + + // On older platforms, returning std::unique_ptr + // when the return type is std::unique_ptr + // doesn't compile without a std::move call. However, this causes + // a warning on newer platforms. Thus, we have to work around + // it a bit and downcast the pointer manually. + auto ret = std::unique_ptr(new ListeningReporter); + auto &multi = static_cast(*ret); + auto const &listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); + for (auto const &listener : listeners) { + multi.addListener(listener->create(Catch::ReporterConfig(config))); + } + multi.addReporter(createReporter(config->getReporterName(), config)); + return ret; + } + + class TestGroup { + public: + explicit TestGroup(std::shared_ptr const &config) + : m_config{config} + , m_context{config, makeReporter(config)} { + auto const &allTestCases = getAllTestCasesSorted(*m_config); + m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config); + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + + if (m_matches.empty() && invalidArgs.empty()) { + for (auto const &test : allTestCases) + if (!test.isHidden()) + m_tests.emplace(&test); + } else { + for (auto const &match : m_matches) + m_tests.insert(match.tests.begin(), match.tests.end()); + } + } + + Totals execute() { + auto const &invalidArgs = m_config->testSpec().getInvalidArgs(); + Totals totals; + m_context.testGroupStarting(m_config->name(), 1, 1); + for (auto const &testCase : m_tests) { + if (!m_context.aborting()) + totals += m_context.runTest(*testCase); + else + m_context.reporter().skipTest(*testCase); } -#ifndef CATCH_CONFIG_DEFAULT_REPORTER -#define CATCH_CONFIG_DEFAULT_REPORTER "console" + for (auto const &match : m_matches) { + if (match.tests.empty()) { + m_context.reporter().noMatchingTestCases(match.name); + totals.error = -1; + } + } + + if (!invalidArgs.empty()) { + for (auto const &invalidArg : invalidArgs) + m_context.reporter().reportInvalidArguments(invalidArg); + } + + m_context.testGroupEnded(m_config->name(), totals, 1, 1); + return totals; + } + + private: + using Tests = std::set; + + std::shared_ptr m_config; + RunContext m_context; + Tests m_tests; + TestSpec::Matches m_matches; + }; + + void applyFilenamesAsTags(Catch::IConfig const &config) { + auto &tests = const_cast &>(getAllTestCasesSorted(config)); + for (auto &testCase : tests) { + auto tags = testCase.tags; + + std::string filename = testCase.lineInfo.file; + auto lastSlash = filename.find_last_of("\\/"); + if (lastSlash != std::string::npos) { + filename.erase(0, lastSlash); + filename[0] = '#'; + } + + auto lastDot = filename.find_last_of('.'); + if (lastDot != std::string::npos) { + filename.erase(lastDot); + } + + tags.push_back(std::move(filename)); + setTags(testCase, tags); + } + } + + } // namespace + + Session::Session() { + static bool alreadyInstantiated = false; + if (alreadyInstantiated) { + CATCH_TRY { CATCH_INTERNAL_ERROR("Only one instance of Catch::Session can ever be used"); } + CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); } + } + +// There cannot be exceptions at startup in no-exception mode. +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + const auto &exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); + if (!exceptions.empty()) { + config(); + getCurrentMutableContext().setConfig(m_config); + + m_startupExceptions = true; + Colour colourGuard(Colour::Red); + Catch::cerr() << "Errors occurred during startup!" << '\n'; + // iterate over all exceptions and notify user + for (const auto &ex_ptr : exceptions) { + try { + std::rethrow_exception(ex_ptr); + } catch (std::exception const &ex) { + Catch::cerr() << Column(ex.what()).indent(2) << '\n'; + } + } + } #endif - IStreamingReporterPtr makeReporter(std::shared_ptr const& config) { - auto const& reporterNames = config->getReporterNames(); - if (reporterNames.empty()) - return createReporter(CATCH_CONFIG_DEFAULT_REPORTER, config); + alreadyInstantiated = true; + m_cli = makeCommandLineParser(m_configData); + } + Session::~Session() { + Catch::cleanUp(); + } - IStreamingReporterPtr reporter; - for (auto const& name : reporterNames) - addReporter(reporter, createReporter(name, config)); - return reporter; - } + void Session::showHelp() const { + Catch::cout() << "\nCatch v" << libraryVersion() << "\n" + << m_cli << std::endl + << "For more detailed usage please see the project docs\n" + << std::endl; + } + void Session::libIdentify() { + Catch::cout() << std::left << std::setw(16) << "description: " + << "A Catch2 test executable\n" + << std::left << std::setw(16) << "category: " + << "testframework\n" + << std::left << std::setw(16) << "framework: " + << "Catch Test\n" + << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + } -#undef CATCH_CONFIG_DEFAULT_REPORTER + int Session::applyCommandLine(int argc, char const *const *argv) { + if (m_startupExceptions) + return 1; - void addListeners(IStreamingReporterPtr& reporters, IConfigPtr const& config) { - auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners(); - for (auto const& listener : listeners) - addReporter(reporters, listener->create(Catch::ReporterConfig(config))); - } - - Catch::Totals runTests(std::shared_ptr const& config) { - IStreamingReporterPtr reporter = makeReporter(config); - addListeners(reporter, config); - - RunContext context(config, std::move(reporter)); - - Totals totals; - - context.testGroupStarting(config->name(), 1, 1); - - TestSpec testSpec = config->testSpec(); - - auto const& allTestCases = getAllTestCasesSorted(*config); - for (auto const& testCase : allTestCases) { - if (!context.aborting() && matchTest(testCase, testSpec, *config)) - totals += context.runTest(testCase); - else - context.reporter().skipTest(testCase); - } - - if (config->warnAboutNoTests() && totals.testCases.total() == 0) { - ReusableStringStream testConfig; - - bool first = true; - for (const auto& input : config->getTestsOrTags()) { - if (!first) { testConfig << ' '; } - first = false; - testConfig << input; - } - - context.reporter().noMatchingTestCases(testConfig.str()); - totals.error = -1; - } - - context.testGroupEnded(config->name(), totals, 1, 1); - return totals; - } - - void applyFilenamesAsTags(Catch::IConfig const& config) { - auto& tests = const_cast&>(getAllTestCasesSorted(config)); - for (auto& testCase : tests) { - auto tags = testCase.tags; - - std::string filename = testCase.lineInfo.file; - auto lastSlash = filename.find_last_of("\\/"); - if (lastSlash != std::string::npos) { - filename.erase(0, lastSlash); - filename[0] = '#'; - } - - auto lastDot = filename.find_last_of('.'); - if (lastDot != std::string::npos) { - filename.erase(lastDot); - } - - tags.push_back(std::move(filename)); - setTags(testCase, tags); - } - } - - } // anon namespace - - Session::Session() { - static bool alreadyInstantiated = false; - if( alreadyInstantiated ) { - try { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); } - catch(...) { getMutableRegistryHub().registerStartupException(); } - } - - const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions(); - if ( !exceptions.empty() ) { - m_startupExceptions = true; - Colour colourGuard( Colour::Red ); - Catch::cerr() << "Errors occurred during startup!" << '\n'; - // iterate over all exceptions and notify user - for ( const auto& ex_ptr : exceptions ) { - try { - std::rethrow_exception(ex_ptr); - } catch ( std::exception const& ex ) { - Catch::cerr() << Column( ex.what() ).indent(2) << '\n'; - } - } - } - - alreadyInstantiated = true; - m_cli = makeCommandLineParser( m_configData ); - } - Session::~Session() { - Catch::cleanUp(); + auto result = m_cli.parse(clara::Args(argc, argv)); + if (!result) { + config(); + getCurrentMutableContext().setConfig(m_config); + Catch::cerr() << Colour(Colour::Red) << "\nError(s) in input:\n" + << Column(result.errorMessage()).indent(2) << "\n\n"; + Catch::cerr() << "Run with -? for usage\n" + << std::endl; + return MaxExitCode; } - void Session::showHelp() const { - Catch::cout() - << "\nCatch v" << libraryVersion() << "\n" - << m_cli << std::endl - << "For more detailed usage please see the project docs\n" << std::endl; - } - void Session::libIdentify() { - Catch::cout() - << std::left << std::setw(16) << "description: " << "A Catch test executable\n" - << std::left << std::setw(16) << "category: " << "testframework\n" - << std::left << std::setw(16) << "framework: " << "Catch Test\n" - << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl; + if (m_configData.showHelp) + showHelp(); + if (m_configData.libIdentify) + libIdentify(); + m_config.reset(); + return 0; + } + +#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE) + int Session::applyCommandLine(int argc, wchar_t const *const *argv) { + char **utf8Argv = new char *[argc]; + + for (int i = 0; i < argc; ++i) { + int bufSize = WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr); + + utf8Argv[i] = new char[bufSize]; + + WideCharToMultiByte(CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr); } - int Session::applyCommandLine( int argc, char const * const * argv ) { - if( m_startupExceptions ) - return 1; + int returnCode = applyCommandLine(argc, utf8Argv); - auto result = m_cli.parse( clara::Args( argc, argv ) ); - if( !result ) { - Catch::cerr() - << Colour( Colour::Red ) - << "\nError(s) in input:\n" - << Column( result.errorMessage() ).indent( 2 ) - << "\n\n"; - Catch::cerr() << "Run with -? for usage\n" << std::endl; - return MaxExitCode; - } + for (int i = 0; i < argc; ++i) + delete[] utf8Argv[i]; - if( m_configData.showHelp ) - showHelp(); - if( m_configData.libIdentify ) - libIdentify(); - m_config.reset(); - return 0; + delete[] utf8Argv; + + return returnCode; + } +#endif + + void Session::useConfigData(ConfigData const &configData) { + m_configData = configData; + m_config.reset(); + } + + int Session::run() { + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeStart) != 0) { + Catch::cout() << "...waiting for enter/ return before starting" << std::endl; + static_cast(std::getchar()); + } + int exitCode = runInternal(); + if ((m_configData.waitForKeypress & WaitForKeypress::BeforeExit) != 0) { + Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; + static_cast(std::getchar()); + } + return exitCode; + } + + clara::Parser const &Session::cli() const { + return m_cli; + } + void Session::cli(clara::Parser const &newParser) { + m_cli = newParser; + } + ConfigData &Session::configData() { + return m_configData; + } + Config &Session::config() { + if (!m_config) + m_config = std::make_shared(m_configData); + return *m_config; + } + + int Session::runInternal() { + if (m_startupExceptions) + return 1; + + if (m_configData.showHelp || m_configData.libIdentify) { + return 0; } - void Session::useConfigData( ConfigData const& configData ) { - m_configData = configData; - m_config.reset(); + CATCH_TRY { + config(); // Force config to be constructed + + seedRng(*m_config); + + if (m_configData.filenamesAsTags) + applyFilenamesAsTags(*m_config); + + // Handle list request + if (Option listed = list(m_config)) + return static_cast(*listed); + + TestGroup tests{m_config}; + auto const totals = tests.execute(); + + if (m_config->warnAboutNoTests() && totals.error == -1) + return 2; + + // Note that on unices only the lower 8 bits are usually used, clamping + // the return value to 255 prevents false negative when some multiple + // of 256 tests has failed + return (std::min)(MaxExitCode, (std::max)(totals.error, static_cast(totals.assertions.failed))); } - - int Session::run( int argc, char* argv[] ) { - if( m_startupExceptions ) - return 1; - int returnCode = applyCommandLine( argc, argv ); - if( returnCode == 0 ) - returnCode = run(); - return returnCode; - } - -#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE) - int Session::run( int argc, wchar_t* const argv[] ) { - - char **utf8Argv = new char *[ argc ]; - - for ( int i = 0; i < argc; ++i ) { - int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL ); - - utf8Argv[ i ] = new char[ bufSize ]; - - WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL ); - } - - int returnCode = run( argc, utf8Argv ); - - for ( int i = 0; i < argc; ++i ) - delete [] utf8Argv[ i ]; - - delete [] utf8Argv; - - return returnCode; +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + catch (std::exception &ex) { + Catch::cerr() << ex.what() << std::endl; + return MaxExitCode; } #endif - int Session::run() { - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before starting" << std::endl; - static_cast(std::getchar()); - } - int exitCode = runInternal(); - if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) { - Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl; - static_cast(std::getchar()); - } - return exitCode; - } - - clara::Parser const& Session::cli() const { - return m_cli; - } - void Session::cli( clara::Parser const& newParser ) { - m_cli = newParser; - } - ConfigData& Session::configData() { - return m_configData; - } - Config& Session::config() { - if( !m_config ) - m_config = std::make_shared( m_configData ); - return *m_config; - } - - int Session::runInternal() { - if( m_startupExceptions ) - return 1; - - if( m_configData.showHelp || m_configData.libIdentify ) - return 0; - - try - { - config(); // Force config to be constructed - - seedRng( *m_config ); - - if( m_configData.filenamesAsTags ) - applyFilenamesAsTags( *m_config ); - - // Handle list request - if( Option listed = list( config() ) ) - return static_cast( *listed ); - - auto totals = runTests( m_config ); - // Note that on unices only the lower 8 bits are usually used, clamping - // the return value to 255 prevents false negative when some multiple - // of 256 tests has failed - return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast(totals.assertions.failed))); - } - catch( std::exception& ex ) { - Catch::cerr() << ex.what() << std::endl; - return MaxExitCode; - } - } + } } // end namespace Catch // end catch_session.cpp -// start catch_startup_exception_registry.cpp +// start catch_singletons.cpp + +#include namespace Catch { - void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept { - try { - m_exceptions.push_back(exception); - } - catch(...) { - // If we run out of memory during start-up there's really not a lot more we can do about it - std::terminate(); - } + namespace { + static auto getSingletons() -> std::vector *& { + static std::vector *g_singletons = nullptr; + if (!g_singletons) + g_singletons = new std::vector(); + return g_singletons; } + } // namespace - std::vector const& StartupExceptionRegistry::getExceptions() const noexcept { - return m_exceptions; + ISingleton::~ISingleton() {} + + void addSingleton(ISingleton *singleton) { + getSingletons()->push_back(singleton); + } + void cleanupSingletons() { + auto &singletons = getSingletons(); + for (auto singleton : *singletons) + delete singleton; + delete singletons; + singletons = nullptr; + } + +} // namespace Catch +// end catch_singletons.cpp +// start catch_startup_exception_registry.cpp + +#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +namespace Catch { + void StartupExceptionRegistry::add(std::exception_ptr const &exception) noexcept { + CATCH_TRY { m_exceptions.push_back(exception); } + CATCH_CATCH_ALL { + // If we run out of memory during start-up there's really not a lot more we can do about it + std::terminate(); } + } + + std::vector const &StartupExceptionRegistry::getExceptions() const noexcept { + return m_exceptions; + } } // end namespace Catch +#endif // end catch_startup_exception_registry.cpp // start catch_stream.cpp #include -#include #include +#include +#include #include #include -#include - -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" -#endif namespace Catch { + Catch::IStream::~IStream() = default; - Catch::IStream::~IStream() = default; + namespace Detail { + namespace { + template + class StreamBufImpl : public std::streambuf { + char data[bufferSize]; + WriterF m_writer; - namespace detail { namespace { - template - class StreamBufImpl : public std::streambuf { - char data[bufferSize]; - WriterF m_writer; + public: + StreamBufImpl() { setp(data, data + sizeof(data)); } - public: - StreamBufImpl() { - setp( data, data + sizeof(data) ); - } + ~StreamBufImpl() noexcept { StreamBufImpl::sync(); } - ~StreamBufImpl() noexcept { - StreamBufImpl::sync(); - } + private: + int overflow(int c) override { + sync(); - private: - int overflow( int c ) override { - sync(); - - if( c != EOF ) { - if( pbase() == epptr() ) - m_writer( std::string( 1, static_cast( c ) ) ); - else - sputc( static_cast( c ) ); - } - return 0; - } - - int sync() override { - if( pbase() != pptr() ) { - m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); - setp( pbase(), epptr() ); - } - return 0; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - struct OutputDebugWriter { - - void operator()( std::string const&str ) { - writeToDebugConsole( str ); - } - }; - - /////////////////////////////////////////////////////////////////////////// - - class FileStream : public IStream { - mutable std::ofstream m_ofs; - public: - FileStream( StringRef filename ) { - m_ofs.open( filename.c_str() ); - CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" ); - } - ~FileStream() override = default; - public: // IStream - std::ostream& stream() const override { - return m_ofs; - } - }; - - /////////////////////////////////////////////////////////////////////////// - - class CoutStream : public IStream { - mutable std::ostream m_os; - public: - // Store the streambuf from cout up-front because - // cout may get redirected when running tests - CoutStream() : m_os( Catch::cout().rdbuf() ) {} - ~CoutStream() override = default; - - public: // IStream - std::ostream& stream() const override { return m_os; } - }; - - /////////////////////////////////////////////////////////////////////////// - - class DebugOutStream : public IStream { - std::unique_ptr> m_streamBuf; - mutable std::ostream m_os; - public: - DebugOutStream() - : m_streamBuf( new StreamBufImpl() ), - m_os( m_streamBuf.get() ) - {} - - ~DebugOutStream() override = default; - - public: // IStream - std::ostream& stream() const override { return m_os; } - }; - - }} // namespace anon::detail - - /////////////////////////////////////////////////////////////////////////// - - auto makeStream( StringRef const &filename ) -> IStream const* { - if( filename.empty() ) - return new detail::CoutStream(); - else if( filename[0] == '%' ) { - if( filename == "%debug" ) - return new detail::DebugOutStream(); + if (c != EOF) { + if (pbase() == epptr()) + m_writer(std::string(1, static_cast(c))); else - CATCH_ERROR( "Unrecognised stream: '" << filename << "'" ); + sputc(static_cast(c)); + } + return 0; } - else - return new detail::FileStream( filename ); + + int sync() override { + if (pbase() != pptr()) { + m_writer(std::string(pbase(), static_cast(pptr() - pbase()))); + setp(pbase(), epptr()); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + struct OutputDebugWriter { + void operator()(std::string const &str) { writeToDebugConsole(str); } + }; + + /////////////////////////////////////////////////////////////////////////// + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + + public: + FileStream(StringRef filename) { + m_ofs.open(filename.c_str()); + CATCH_ENFORCE(!m_ofs.fail(), "Unable to open file: '" << filename << "'"); + } + ~FileStream() override = default; + + public: // IStream + std::ostream &stream() const override { return m_ofs; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class CoutStream : public IStream { + mutable std::ostream m_os; + + public: + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream() + : m_os(Catch::cout().rdbuf()) { + } + ~CoutStream() override = default; + + public: // IStream + std::ostream &stream() const override { return m_os; } + }; + + /////////////////////////////////////////////////////////////////////////// + + class DebugOutStream : public IStream { + std::unique_ptr> m_streamBuf; + mutable std::ostream m_os; + + public: + DebugOutStream() + : m_streamBuf(new StreamBufImpl()) + , m_os(m_streamBuf.get()) { + } + + ~DebugOutStream() override = default; + + public: // IStream + std::ostream &stream() const override { return m_os; } + }; + + } // namespace + } // namespace Detail + + /////////////////////////////////////////////////////////////////////////// + + auto makeStream(StringRef const &filename) -> IStream const * { + if (filename.empty()) + return new Detail::CoutStream(); + else if (filename[0] == '%') { + if (filename == "%debug") + return new Detail::DebugOutStream(); + else + CATCH_ERROR("Unrecognised stream: '" << filename << "'"); + } else + return new Detail::FileStream(filename); + } + + // This class encapsulates the idea of a pool of ostringstreams that can be reused. + struct StringStreams { + std::vector> m_streams; + std::vector m_unused; + std::ostringstream m_referenceStream; // Used for copy state/ flags from + + auto add() -> std::size_t { + if (m_unused.empty()) { + m_streams.push_back(std::unique_ptr(new std::ostringstream)); + return m_streams.size() - 1; + } else { + auto index = m_unused.back(); + m_unused.pop_back(); + return index; + } } - // This class encapsulates the idea of a pool of ostringstreams that can be reused. - struct StringStreams { - std::vector> m_streams; - std::vector m_unused; - std::ostringstream m_referenceStream; // Used for copy state/ flags from - static StringStreams* s_instance; - - auto add() -> std::size_t { - if( m_unused.empty() ) { - m_streams.push_back( std::unique_ptr( new std::ostringstream ) ); - return m_streams.size()-1; - } - else { - auto index = m_unused.back(); - m_unused.pop_back(); - return index; - } - } - - void release( std::size_t index ) { - m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state - m_unused.push_back(index); - } - - // !TBD: put in TLS - static auto instance() -> StringStreams& { - if( !s_instance ) - s_instance = new StringStreams(); - return *s_instance; - } - static void cleanup() { - delete s_instance; - s_instance = nullptr; - } - }; - - StringStreams* StringStreams::s_instance = nullptr; - - void ReusableStringStream::cleanup() { - StringStreams::cleanup(); + void release(std::size_t index) { + m_streams[index]->copyfmt(m_referenceStream); // Restore initial flags and other state + m_unused.push_back(index); } + }; - ReusableStringStream::ReusableStringStream() - : m_index( StringStreams::instance().add() ), - m_oss( StringStreams::instance().m_streams[m_index].get() ) - {} + ReusableStringStream::ReusableStringStream() + : m_index(Singleton::getMutable().add()) + , m_oss(Singleton::getMutable().m_streams[m_index].get()) { + } - ReusableStringStream::~ReusableStringStream() { - static_cast( m_oss )->str(""); - m_oss->clear(); - StringStreams::instance().release( m_index ); - } + ReusableStringStream::~ReusableStringStream() { + static_cast(m_oss)->str(""); + m_oss->clear(); + Singleton::getMutable().release(m_index); + } - auto ReusableStringStream::str() const -> std::string { - return static_cast( m_oss )->str(); - } + auto ReusableStringStream::str() const -> std::string { + return static_cast(m_oss)->str(); + } - /////////////////////////////////////////////////////////////////////////// + /////////////////////////////////////////////////////////////////////////// #ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions - std::ostream& cout() { return std::cout; } - std::ostream& cerr() { return std::cerr; } - std::ostream& clog() { return std::clog; } -#endif -} - -#if defined(__clang__) -# pragma clang diagnostic pop + std::ostream &cout() { + return std::cout; + } + std::ostream &cerr() { + return std::cerr; + } + std::ostream &clog() { + return std::clog; + } #endif +} // namespace Catch // end catch_stream.cpp // start catch_string_manip.cpp #include -#include -#include #include +#include +#include +#include namespace Catch { - - bool startsWith( std::string const& s, std::string const& prefix ) { - return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); - } - bool startsWith( std::string const& s, char prefix ) { - return !s.empty() && s[0] == prefix; - } - bool endsWith( std::string const& s, std::string const& suffix ) { - return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); - } - bool endsWith( std::string const& s, char suffix ) { - return !s.empty() && s[s.size()-1] == suffix; - } - bool contains( std::string const& s, std::string const& infix ) { - return s.find( infix ) != std::string::npos; - } + namespace { char toLowerCh(char c) { - return static_cast( std::tolower( c ) ); + return static_cast(std::tolower(static_cast(c))); } - void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); - } - std::string toLower( std::string const& s ) { - std::string lc = s; - toLowerInPlace( lc ); - return lc; - } - std::string trim( std::string const& str ) { - static char const* whitespaceChars = "\n\r\t "; - std::string::size_type start = str.find_first_not_of( whitespaceChars ); - std::string::size_type end = str.find_last_not_of( whitespaceChars ); + } // namespace - return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + bool startsWith(std::string const &s, std::string const &prefix) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith(std::string const &s, char prefix) { + return !s.empty() && s[0] == prefix; + } + bool endsWith(std::string const &s, std::string const &suffix) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith(std::string const &s, char suffix) { + return !s.empty() && s[s.size() - 1] == suffix; + } + bool contains(std::string const &s, std::string const &infix) { + return s.find(infix) != std::string::npos; + } + void toLowerInPlace(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), toLowerCh); + } + std::string toLower(std::string const &s) { + std::string lc = s; + toLowerInPlace(lc); + return lc; + } + std::string trim(std::string const &str) { + static char const *whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of(whitespaceChars); + std::string::size_type end = str.find_last_not_of(whitespaceChars); + + return start != std::string::npos ? str.substr(start, 1 + end - start) : std::string(); + } + + StringRef trim(StringRef ref) { + const auto is_ws = [](char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }; + size_t real_begin = 0; + while (real_begin < ref.size() && is_ws(ref[real_begin])) { + ++real_begin; + } + size_t real_end = ref.size(); + while (real_end > real_begin && is_ws(ref[real_end - 1])) { + --real_end; } - bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { - bool replaced = false; - std::size_t i = str.find( replaceThis ); - while( i != std::string::npos ) { - replaced = true; - str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); - if( i < str.size()-withThis.size() ) - i = str.find( replaceThis, i+withThis.size() ); - else - i = std::string::npos; - } - return replaced; + return ref.substr(real_begin, real_end - real_begin); + } + + bool replaceInPlace(std::string &str, std::string const &replaceThis, std::string const &withThis) { + bool replaced = false; + std::size_t i = str.find(replaceThis); + while (i != std::string::npos) { + replaced = true; + str = str.substr(0, i) + withThis + str.substr(i + replaceThis.size()); + if (i < str.size() - withThis.size()) + i = str.find(replaceThis, i + withThis.size()); + else + i = std::string::npos; } + return replaced; + } - pluralise::pluralise( std::size_t count, std::string const& label ) - : m_count( count ), - m_label( label ) - {} - - std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { - os << pluraliser.m_count << ' ' << pluraliser.m_label; - if( pluraliser.m_count != 1 ) - os << 's'; - return os; + std::vector splitStringRef(StringRef str, char delimiter) { + std::vector subStrings; + std::size_t start = 0; + for (std::size_t pos = 0; pos < str.size(); ++pos) { + if (str[pos] == delimiter) { + if (pos - start > 1) + subStrings.push_back(str.substr(start, pos - start)); + start = pos + 1; + } } + if (start < str.size()) + subStrings.push_back(str.substr(start, str.size() - start)); + return subStrings; + } -} + pluralise::pluralise(std::size_t count, std::string const &label) + : m_count(count) + , m_label(label) { + } + + std::ostream &operator<<(std::ostream &os, pluralise const &pluraliser) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if (pluraliser.m_count != 1) + os << 's'; + return os; + } + +} // namespace Catch // end catch_string_manip.cpp // start catch_stringref.cpp -#if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" -#endif - -#include -#include +#include #include - -namespace { - const uint32_t byte_2_lead = 0xC0; - const uint32_t byte_3_lead = 0xE0; - const uint32_t byte_4_lead = 0xF0; -} +#include +#include namespace Catch { - StringRef::StringRef( char const* rawChars ) noexcept - : StringRef( rawChars, static_cast(std::strlen(rawChars) ) ) - {} + StringRef::StringRef(char const *rawChars) noexcept + : StringRef(rawChars, static_cast(std::strlen(rawChars))) { + } - StringRef::operator std::string() const { - return std::string( m_start, m_size ); - } + auto StringRef::c_str() const -> char const * { + CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance"); + return m_start; + } + auto StringRef::data() const noexcept -> char const * { + return m_start; + } - void StringRef::swap( StringRef& other ) noexcept { - std::swap( m_start, other.m_start ); - std::swap( m_size, other.m_size ); - std::swap( m_data, other.m_data ); + auto StringRef::substr(size_type start, size_type size) const noexcept -> StringRef { + if (start < m_size) { + return StringRef(m_start + start, (std::min)(m_size - start, size)); + } else { + return StringRef(); } + } + auto StringRef::operator==(StringRef const &other) const noexcept -> bool { + return m_size == other.m_size && (std::memcmp(m_start, other.m_start, m_size) == 0); + } - auto StringRef::c_str() const -> char const* { - if( isSubstring() ) - const_cast( this )->takeOwnership(); - return m_start; - } - auto StringRef::currentData() const noexcept -> char const* { - return m_start; - } + auto operator<<(std::ostream &os, StringRef const &str) -> std::ostream & { + return os.write(str.data(), str.size()); + } - auto StringRef::isOwned() const noexcept -> bool { - return m_data != nullptr; - } - auto StringRef::isSubstring() const noexcept -> bool { - return m_start[m_size] != '\0'; - } - - void StringRef::takeOwnership() { - if( !isOwned() ) { - m_data = new char[m_size+1]; - memcpy( m_data, m_start, m_size ); - m_data[m_size] = '\0'; - m_start = m_data; - } - } - auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef { - if( start < m_size ) - return StringRef( m_start+start, size ); - else - return StringRef(); - } - auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool { - return - size() == other.size() && - (std::strncmp( m_start, other.m_start, size() ) == 0); - } - auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool { - return !operator==( other ); - } - - auto StringRef::operator[](size_type index) const noexcept -> char { - return m_start[index]; - } - - auto StringRef::numberOfCharacters() const noexcept -> size_type { - size_type noChars = m_size; - // Make adjustments for uft encodings - for( size_type i=0; i < m_size; ++i ) { - char c = m_start[i]; - if( ( c & byte_2_lead ) == byte_2_lead ) { - noChars--; - if (( c & byte_3_lead ) == byte_3_lead ) - noChars--; - if( ( c & byte_4_lead ) == byte_4_lead ) - noChars--; - } - } - return noChars; - } - - auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string { - std::string str; - str.reserve( lhs.size() + rhs.size() ); - str += lhs; - str += rhs; - return str; - } - auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string { - return std::string( lhs ) + std::string( rhs ); - } - auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string { - return std::string( lhs ) + std::string( rhs ); - } - - auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& { - return os.write(str.currentData(), str.size()); - } - - auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& { - lhs.append(rhs.currentData(), rhs.size()); - return lhs; - } + auto operator+=(std::string &lhs, StringRef const &rhs) -> std::string & { + lhs.append(rhs.data(), rhs.size()); + return lhs; + } } // namespace Catch - -#if defined(__clang__) -# pragma clang diagnostic pop -#endif // end catch_stringref.cpp // start catch_tag_alias.cpp namespace Catch { - TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {} -} + TagAlias::TagAlias(std::string const &_tag, SourceLineInfo _lineInfo) + : tag(_tag) + , lineInfo(_lineInfo) { + } +} // namespace Catch // end catch_tag_alias.cpp // start catch_tag_alias_autoregistrar.cpp namespace Catch { - - RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) { - try { - getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); - } catch (...) { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } + RegistrarForTagAliases::RegistrarForTagAliases(char const *alias, char const *tag, SourceLineInfo const &lineInfo) { + CATCH_TRY { getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo); } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); } + } -} +} // namespace Catch // end catch_tag_alias_autoregistrar.cpp // start catch_tag_alias_registry.cpp #include namespace Catch { + TagAliasRegistry::~TagAliasRegistry() {} - TagAliasRegistry::~TagAliasRegistry() {} + TagAlias const *TagAliasRegistry::find(std::string const &alias) const { + auto it = m_registry.find(alias); + if (it != m_registry.end()) + return &(it->second); + else + return nullptr; + } - TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { - auto it = m_registry.find( alias ); - if( it != m_registry.end() ) - return &(it->second); - else - return nullptr; + std::string TagAliasRegistry::expandAliases(std::string const &unexpandedTestSpec) const { + std::string expandedTestSpec = unexpandedTestSpec; + for (auto const ®istryKvp : m_registry) { + std::size_t pos = expandedTestSpec.find(registryKvp.first); + if (pos != std::string::npos) { + expandedTestSpec + = expandedTestSpec.substr(0, pos) + registryKvp.second.tag + expandedTestSpec.substr(pos + registryKvp.first.size()); + } } + return expandedTestSpec; + } - std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { - std::string expandedTestSpec = unexpandedTestSpec; - for( auto const& registryKvp : m_registry ) { - std::size_t pos = expandedTestSpec.find( registryKvp.first ); - if( pos != std::string::npos ) { - expandedTestSpec = expandedTestSpec.substr( 0, pos ) + - registryKvp.second.tag + - expandedTestSpec.substr( pos + registryKvp.first.size() ); - } - } - return expandedTestSpec; - } + void TagAliasRegistry::add(std::string const &alias, std::string const &tag, SourceLineInfo const &lineInfo) { + CATCH_ENFORCE(startsWith(alias, "[@") && endsWith(alias, ']'), + "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" + << lineInfo); - void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) { - CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'), - "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo ); + CATCH_ENFORCE(m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, + "error: tag alias, '" << alias << "' already registered.\n" + << "\tFirst seen at: " << find(alias)->lineInfo << "\n" + << "\tRedefined at: " << lineInfo); + } - CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second, - "error: tag alias, '" << alias << "' already registered.\n" - << "\tFirst seen at: " << find(alias)->lineInfo << "\n" - << "\tRedefined at: " << lineInfo ); - } + ITagAliasRegistry::~ITagAliasRegistry() {} - ITagAliasRegistry::~ITagAliasRegistry() {} - - ITagAliasRegistry const& ITagAliasRegistry::get() { - return getRegistryHub().getTagAliasRegistry(); - } + ITagAliasRegistry const &ITagAliasRegistry::get() { + return getRegistryHub().getTagAliasRegistry(); + } } // end namespace Catch // end catch_tag_alias_registry.cpp // start catch_test_case_info.cpp +#include #include #include -#include #include namespace Catch { + namespace { + TestCaseInfo::SpecialProperties parseSpecialTag(std::string const &tag) { + if (startsWith(tag, '.') || tag == "!hide") + return TestCaseInfo::IsHidden; + else if (tag == "!throws") + return TestCaseInfo::Throws; + else if (tag == "!shouldfail") + return TestCaseInfo::ShouldFail; + else if (tag == "!mayfail") + return TestCaseInfo::MayFail; + else if (tag == "!nonportable") + return TestCaseInfo::NonPortable; + else if (tag == "!benchmark") + return static_cast(TestCaseInfo::Benchmark | TestCaseInfo::IsHidden); + else + return TestCaseInfo::None; + } + bool isReservedTag(std::string const &tag) { + return parseSpecialTag(tag) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum(static_cast(tag[0])); + } + void enforceNotReservedTag(std::string const &tag, SourceLineInfo const &_lineInfo) { + CATCH_ENFORCE(!isReservedTag(tag), + "Tag name: [" << tag << "] is not allowed.\n" + << "Tag names starting with non alphanumeric characters are reserved\n" + << _lineInfo); + } + } // namespace - TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { - if( startsWith( tag, '.' ) || - tag == "!hide" ) - return TestCaseInfo::IsHidden; - else if( tag == "!throws" ) - return TestCaseInfo::Throws; - else if( tag == "!shouldfail" ) - return TestCaseInfo::ShouldFail; - else if( tag == "!mayfail" ) - return TestCaseInfo::MayFail; - else if( tag == "!nonportable" ) - return TestCaseInfo::NonPortable; - else if( tag == "!benchmark" ) - return static_cast( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden ); + TestCase makeTestCase(ITestInvoker *_testCase, std::string const &_className, NameAndTags const &nameAndTags, SourceLineInfo const &_lineInfo) { + bool isHidden = false; + + // Parse out tags + std::vector tags; + std::string desc, tag; + bool inTag = false; + for (char c : nameAndTags.tags) { + if (!inTag) { + if (c == '[') + inTag = true; else - return TestCaseInfo::None; + desc += c; + } else { + if (c == ']') { + TestCaseInfo::SpecialProperties prop = parseSpecialTag(tag); + if ((prop & TestCaseInfo::IsHidden) != 0) + isHidden = true; + else if (prop == TestCaseInfo::None) + enforceNotReservedTag(tag, _lineInfo); + + // Merged hide tags like `[.approvals]` should be added as + // `[.][approvals]`. The `[.]` is added at later point, so + // we only strip the prefix + if (startsWith(tag, '.') && tag.size() > 1) { + tag.erase(0, 1); + } + tags.push_back(tag); + tag.clear(); + inTag = false; + } else + tag += c; + } } - bool isReservedTag( std::string const& tag ) { - return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); - } - void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { - CATCH_ENFORCE( !isReservedTag(tag), - "Tag name: [" << tag << "] is not allowed.\n" - << "Tag names starting with non alpha-numeric characters are reserved\n" - << _lineInfo ); + if (isHidden) { + // Add all "hidden" tags to make them behave identically + tags.insert(tags.end(), {".", "!hide"}); } - TestCase makeTestCase( ITestInvoker* _testCase, - std::string const& _className, - NameAndTags const& nameAndTags, - SourceLineInfo const& _lineInfo ) - { - bool isHidden = false; + TestCaseInfo info(static_cast(nameAndTags.name), _className, desc, tags, _lineInfo); + return TestCase(_testCase, std::move(info)); + } - // Parse out tags - std::vector tags; - std::string desc, tag; - bool inTag = false; - std::string _descOrTags = nameAndTags.tags; - for (char c : _descOrTags) { - if( !inTag ) { - if( c == '[' ) - inTag = true; - else - desc += c; - } - else { - if( c == ']' ) { - TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); - if( ( prop & TestCaseInfo::IsHidden ) != 0 ) - isHidden = true; - else if( prop == TestCaseInfo::None ) - enforceNotReservedTag( tag, _lineInfo ); + void setTags(TestCaseInfo &testCaseInfo, std::vector tags) { + std::sort(begin(tags), end(tags)); + tags.erase(std::unique(begin(tags), end(tags)), end(tags)); + testCaseInfo.lcaseTags.clear(); - tags.push_back( tag ); - tag.clear(); - inTag = false; - } - else - tag += c; - } - } - if( isHidden ) { - tags.push_back( "." ); - } + for (auto const &tag : tags) { + std::string lcaseTag = toLower(tag); + testCaseInfo.properties = static_cast(testCaseInfo.properties | parseSpecialTag(lcaseTag)); + testCaseInfo.lcaseTags.push_back(lcaseTag); + } + testCaseInfo.tags = std::move(tags); + } - TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo ); - return TestCase( _testCase, std::move(info) ); + TestCaseInfo::TestCaseInfo(std::string const &_name, + std::string const &_className, + std::string const &_description, + std::vector const &_tags, + SourceLineInfo const &_lineInfo) + : name(_name) + , className(_className) + , description(_description) + , lineInfo(_lineInfo) + , properties(None) { + setTags(*this, _tags); + } + + bool TestCaseInfo::isHidden() const { + return (properties & IsHidden) != 0; + } + bool TestCaseInfo::throws() const { + return (properties & Throws) != 0; + } + bool TestCaseInfo::okToFail() const { + return (properties & (ShouldFail | MayFail)) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return (properties & (ShouldFail)) != 0; + } + + std::string TestCaseInfo::tagsAsString() const { + std::string ret; + // '[' and ']' per tag + std::size_t full_size = 2 * tags.size(); + for (const auto &tag : tags) { + full_size += tag.size(); + } + ret.reserve(full_size); + for (const auto &tag : tags) { + ret.push_back('['); + ret.append(tag); + ret.push_back(']'); } - void setTags( TestCaseInfo& testCaseInfo, std::vector tags ) { - std::sort(begin(tags), end(tags)); - tags.erase(std::unique(begin(tags), end(tags)), end(tags)); - testCaseInfo.lcaseTags.clear(); + return ret; + } - for( auto const& tag : tags ) { - std::string lcaseTag = toLower( tag ); - testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); - testCaseInfo.lcaseTags.push_back( lcaseTag ); - } - testCaseInfo.tags = std::move(tags); - } + TestCase::TestCase(ITestInvoker *testCase, TestCaseInfo &&info) + : TestCaseInfo(std::move(info)) + , test(testCase) { + } - TestCaseInfo::TestCaseInfo( std::string const& _name, - std::string const& _className, - std::string const& _description, - std::vector const& _tags, - SourceLineInfo const& _lineInfo ) - : name( _name ), - className( _className ), - description( _description ), - lineInfo( _lineInfo ), - properties( None ) - { - setTags( *this, _tags ); - } + TestCase TestCase::withName(std::string const &_newName) const { + TestCase other(*this); + other.name = _newName; + return other; + } - bool TestCaseInfo::isHidden() const { - return ( properties & IsHidden ) != 0; - } - bool TestCaseInfo::throws() const { - return ( properties & Throws ) != 0; - } - bool TestCaseInfo::okToFail() const { - return ( properties & (ShouldFail | MayFail ) ) != 0; - } - bool TestCaseInfo::expectedToFail() const { - return ( properties & (ShouldFail ) ) != 0; - } + void TestCase::invoke() const { + test->invoke(); + } - std::string TestCaseInfo::tagsAsString() const { - std::string ret; - // '[' and ']' per tag - std::size_t full_size = 2 * tags.size(); - for (const auto& tag : tags) { - full_size += tag.size(); - } - ret.reserve(full_size); - for (const auto& tag : tags) { - ret.push_back('['); - ret.append(tag); - ret.push_back(']'); - } + bool TestCase::operator==(TestCase const &other) const { + return test.get() == other.test.get() && name == other.name && className == other.className; + } - return ret; - } + bool TestCase::operator<(TestCase const &other) const { + return name < other.name; + } - TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {} - - TestCase TestCase::withName( std::string const& _newName ) const { - TestCase other( *this ); - other.name = _newName; - return other; - } - - void TestCase::invoke() const { - test->invoke(); - } - - bool TestCase::operator == ( TestCase const& other ) const { - return test.get() == other.test.get() && - name == other.name && - className == other.className; - } - - bool TestCase::operator < ( TestCase const& other ) const { - return name < other.name; - } - - TestCaseInfo const& TestCase::getTestCaseInfo() const - { - return *this; - } + TestCaseInfo const &TestCase::getTestCaseInfo() const { + return *this; + } } // end namespace Catch // end catch_test_case_info.cpp // start catch_test_case_registry_impl.cpp +#include #include namespace Catch { + namespace { + struct TestHasher { + using hash_t = uint64_t; - std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + explicit TestHasher(hash_t hashSuffix) + : m_hashSuffix{hashSuffix} { + } + uint32_t operator()(TestCase const &t) const { + // FNV-1a hash with multiplication fold. + const hash_t prime = 1099511628211u; + hash_t hash = 14695981039346656037u; + for (const char c : t.name) { + hash ^= c; + hash *= prime; + } + hash ^= m_hashSuffix; + hash *= prime; + const uint32_t low{static_cast(hash)}; + const uint32_t high{static_cast(hash >> 32)}; + return low * high; + } + + private: + hash_t m_hashSuffix; + }; + } // end unnamed namespace + + std::vector sortTests(IConfig const &config, std::vector const &unsortedTestCases) { + switch (config.runOrder()) { + case RunTests::InDeclarationOrder: + // already in declaration order + break; + + case RunTests::InLexicographicalOrder: { std::vector sorted = unsortedTestCases; - - switch( config.runOrder() ) { - case RunTests::InLexicographicalOrder: - std::sort( sorted.begin(), sorted.end() ); - break; - case RunTests::InRandomOrder: - seedRng( config ); - RandomNumberGenerator::shuffle( sorted ); - break; - case RunTests::InDeclarationOrder: - // already in declaration order - break; - } + std::sort(sorted.begin(), sorted.end()); return sorted; - } - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); - } + } - void enforceNoDuplicateTestCases( std::vector const& functions ) { - std::set seenFunctions; - for( auto const& function : functions ) { - auto prev = seenFunctions.insert( function ); - CATCH_ENFORCE( prev.second, + case RunTests::InRandomOrder: { + seedRng(config); + TestHasher h{config.rngSeed()}; + + using hashedTest = std::pair; + std::vector indexed_tests; + indexed_tests.reserve(unsortedTestCases.size()); + + for (auto const &testCase : unsortedTestCases) { + indexed_tests.emplace_back(h(testCase), &testCase); + } + + std::sort(indexed_tests.begin(), indexed_tests.end(), [](hashedTest const &lhs, hashedTest const &rhs) { + if (lhs.first == rhs.first) { + return lhs.second->name < rhs.second->name; + } + return lhs.first < rhs.first; + }); + + std::vector sorted; + sorted.reserve(indexed_tests.size()); + + for (auto const &hashed : indexed_tests) { + sorted.emplace_back(*hashed.second); + } + + return sorted; + } + } + return unsortedTestCases; + } + + bool isThrowSafe(TestCase const &testCase, IConfig const &config) { + return !testCase.throws() || config.allowThrows(); + } + + bool matchTest(TestCase const &testCase, TestSpec const &testSpec, IConfig const &config) { + return testSpec.matches(testCase) && isThrowSafe(testCase, config); + } + + void enforceNoDuplicateTestCases(std::vector const &functions) { + std::set seenFunctions; + for (auto const &function : functions) { + auto prev = seenFunctions.insert(function); + CATCH_ENFORCE(prev.second, "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n" - << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" - << "\tRedefined at " << function.getTestCaseInfo().lineInfo ); - } + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n" + << "\tRedefined at " << function.getTestCaseInfo().lineInfo); } + } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { - std::vector filtered; - filtered.reserve( testCases.size() ); - for( auto const& testCase : testCases ) - if( matchTest( testCase, testSpec, config ) ) - filtered.push_back( testCase ); - return filtered; - } - std::vector const& getAllTestCasesSorted( IConfig const& config ) { - return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + std::vector filterTests(std::vector const &testCases, TestSpec const &testSpec, IConfig const &config) { + std::vector filtered; + filtered.reserve(testCases.size()); + for (auto const &testCase : testCases) { + if ((!testSpec.hasFilters() && !testCase.isHidden()) || (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) { + filtered.push_back(testCase); + } } + return filtered; + } + std::vector const &getAllTestCasesSorted(IConfig const &config) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted(config); + } - void TestRegistry::registerTest( TestCase const& testCase ) { - std::string name = testCase.getTestCaseInfo().name; - if( name.empty() ) { - ReusableStringStream rss; - rss << "Anonymous test case " << ++m_unnamedCount; - return registerTest( testCase.withName( rss.str() ) ); - } - m_functions.push_back( testCase ); + void TestRegistry::registerTest(TestCase const &testCase) { + std::string name = testCase.getTestCaseInfo().name; + if (name.empty()) { + ReusableStringStream rss; + rss << "Anonymous test case " << ++m_unnamedCount; + return registerTest(testCase.withName(rss.str())); } + m_functions.push_back(testCase); + } - std::vector const& TestRegistry::getAllTests() const { - return m_functions; + std::vector const &TestRegistry::getAllTests() const { + return m_functions; + } + std::vector const &TestRegistry::getAllTestsSorted(IConfig const &config) const { + if (m_sortedFunctions.empty()) + enforceNoDuplicateTestCases(m_functions); + + if (m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty()) { + m_sortedFunctions = sortTests(config, m_functions); + m_currentSortOrder = config.runOrder(); } - std::vector const& TestRegistry::getAllTestsSorted( IConfig const& config ) const { - if( m_sortedFunctions.empty() ) - enforceNoDuplicateTestCases( m_functions ); + return m_sortedFunctions; + } - if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { - m_sortedFunctions = sortTests( config, m_functions ); - m_currentSortOrder = config.runOrder(); - } - return m_sortedFunctions; - } - - /////////////////////////////////////////////////////////////////////////// - TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {} - - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - - std::string extractClassName( StringRef const& classOrQualifiedMethodName ) { - std::string className = classOrQualifiedMethodName; - if( startsWith( className, '&' ) ) - { - std::size_t lastColons = className.rfind( "::" ); - std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); - if( penultimateColons == std::string::npos ) - penultimateColons = 1; - className = className.substr( penultimateColons, lastColons-penultimateColons ); - } - return className; + /////////////////////////////////////////////////////////////////////////// + TestInvokerAsFunction::TestInvokerAsFunction(void (*testAsFunction)()) noexcept + : m_testAsFunction(testAsFunction) { + } + + void TestInvokerAsFunction::invoke() const { + m_testAsFunction(); + } + + std::string extractClassName(StringRef const &classOrQualifiedMethodName) { + std::string className(classOrQualifiedMethodName); + if (startsWith(className, '&')) { + std::size_t lastColons = className.rfind("::"); + std::size_t penultimateColons = className.rfind("::", lastColons - 1); + if (penultimateColons == std::string::npos) + penultimateColons = 1; + className = className.substr(penultimateColons, lastColons - penultimateColons); } + return className; + } } // end namespace Catch // end catch_test_case_registry_impl.cpp // start catch_test_case_tracker.cpp #include -#include -#include +#include #include #include +#include #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" #endif namespace Catch { -namespace TestCaseTracking { - - NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) - : name( _name ), - location( _location ) - {} + namespace TestCaseTracking { + NameAndLocation::NameAndLocation(std::string const &_name, SourceLineInfo const &_location) + : name(_name) + , location(_location) { + } ITracker::~ITracker() = default; - TrackerContext& TrackerContext::instance() { - static TrackerContext s_instance; - return s_instance; - } - - ITracker& TrackerContext::startRun() { - m_rootTracker = std::make_shared( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr ); - m_currentTracker = nullptr; - m_runState = Executing; - return *m_rootTracker; + ITracker &TrackerContext::startRun() { + m_rootTracker = std::make_shared(NameAndLocation("{root}", CATCH_INTERNAL_LINEINFO), *this, nullptr); + m_currentTracker = nullptr; + m_runState = Executing; + return *m_rootTracker; } void TrackerContext::endRun() { - m_rootTracker.reset(); - m_currentTracker = nullptr; - m_runState = NotStarted; + m_rootTracker.reset(); + m_currentTracker = nullptr; + m_runState = NotStarted; } void TrackerContext::startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; } void TrackerContext::completeCycle() { - m_runState = CompletedCycle; + m_runState = CompletedCycle; } bool TrackerContext::completedCycle() const { - return m_runState == CompletedCycle; + return m_runState == CompletedCycle; } - ITracker& TrackerContext::currentTracker() { - return *m_currentTracker; + ITracker &TrackerContext::currentTracker() { + return *m_currentTracker; } - void TrackerContext::setCurrentTracker( ITracker* tracker ) { - m_currentTracker = tracker; + void TrackerContext::setCurrentTracker(ITracker *tracker) { + m_currentTracker = tracker; } - TrackerBase::TrackerHasName::TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} - bool TrackerBase::TrackerHasName::operator ()( ITrackerPtr const& tracker ) const { - return - tracker->nameAndLocation().name == m_nameAndLocation.name && - tracker->nameAndLocation().location == m_nameAndLocation.location; + TrackerBase::TrackerBase(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) + : ITracker(nameAndLocation) + , m_ctx(ctx) + , m_parent(parent) { } - TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : m_nameAndLocation( nameAndLocation ), - m_ctx( ctx ), - m_parent( parent ) - {} - - NameAndLocation const& TrackerBase::nameAndLocation() const { - return m_nameAndLocation; - } bool TrackerBase::isComplete() const { - return m_runState == CompletedSuccessfully || m_runState == Failed; + return m_runState == CompletedSuccessfully || m_runState == Failed; } bool TrackerBase::isSuccessfullyCompleted() const { - return m_runState == CompletedSuccessfully; + return m_runState == CompletedSuccessfully; } bool TrackerBase::isOpen() const { - return m_runState != NotStarted && !isComplete(); + return m_runState != NotStarted && !isComplete(); } bool TrackerBase::hasChildren() const { - return !m_children.empty(); + return !m_children.empty(); } - void TrackerBase::addChild( ITrackerPtr const& child ) { - m_children.push_back( child ); + void TrackerBase::addChild(ITrackerPtr const &child) { + m_children.push_back(child); } - ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) { - auto it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); - return( it != m_children.end() ) - ? *it - : nullptr; + ITrackerPtr TrackerBase::findChild(NameAndLocation const &nameAndLocation) { + auto it = std::find_if(m_children.begin(), m_children.end(), [&nameAndLocation](ITrackerPtr const &tracker) { + return tracker->nameAndLocation().location == nameAndLocation.location && tracker->nameAndLocation().name == nameAndLocation.name; + }); + return (it != m_children.end()) ? *it : nullptr; } - ITracker& TrackerBase::parent() { - assert( m_parent ); // Should always be non-null except for root - return *m_parent; + ITracker &TrackerBase::parent() { + assert(m_parent); // Should always be non-null except for root + return *m_parent; } void TrackerBase::openChild() { - if( m_runState != ExecutingChildren ) { - m_runState = ExecutingChildren; - if( m_parent ) - m_parent->openChild(); - } + if (m_runState != ExecutingChildren) { + m_runState = ExecutingChildren; + if (m_parent) + m_parent->openChild(); + } } - bool TrackerBase::isSectionTracker() const { return false; } - bool TrackerBase::isIndexTracker() const { return false; } + bool TrackerBase::isSectionTracker() const { + return false; + } + bool TrackerBase::isGeneratorTracker() const { + return false; + } void TrackerBase::open() { - m_runState = Executing; - moveToThis(); - if( m_parent ) - m_parent->openChild(); + m_runState = Executing; + moveToThis(); + if (m_parent) + m_parent->openChild(); } void TrackerBase::close() { + // Close any still open children (e.g. generators) + while (&m_ctx.currentTracker() != this) + m_ctx.currentTracker().close(); - // Close any still open children (e.g. generators) - while( &m_ctx.currentTracker() != this ) - m_ctx.currentTracker().close(); + switch (m_runState) { + case NeedsAnotherRun: + break; - switch( m_runState ) { - case NeedsAnotherRun: - break; + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if (std::all_of(m_children.begin(), m_children.end(), [](ITrackerPtr const &t) { return t->isComplete(); })) + m_runState = CompletedSuccessfully; + break; - case Executing: - m_runState = CompletedSuccessfully; - break; - case ExecutingChildren: - if( m_children.empty() || m_children.back()->isComplete() ) - m_runState = CompletedSuccessfully; - break; + case NotStarted: + case CompletedSuccessfully: + case Failed: + CATCH_INTERNAL_ERROR("Illogical state: " << m_runState); - case NotStarted: - case CompletedSuccessfully: - case Failed: - CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState ); - - default: - CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState ); - } - moveToParent(); - m_ctx.completeCycle(); + default: + CATCH_INTERNAL_ERROR("Unknown state: " << m_runState); + } + moveToParent(); + m_ctx.completeCycle(); } void TrackerBase::fail() { - m_runState = Failed; - if( m_parent ) - m_parent->markAsNeedingAnotherRun(); - moveToParent(); - m_ctx.completeCycle(); + m_runState = Failed; + if (m_parent) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); } void TrackerBase::markAsNeedingAnotherRun() { - m_runState = NeedsAnotherRun; + m_runState = NeedsAnotherRun; } void TrackerBase::moveToParent() { - assert( m_parent ); - m_ctx.setCurrentTracker( m_parent ); + assert(m_parent); + m_ctx.setCurrentTracker(m_parent); } void TrackerBase::moveToThis() { - m_ctx.setCurrentTracker( this ); + m_ctx.setCurrentTracker(this); } - SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( nameAndLocation, ctx, parent ) - { - if( parent ) { - while( !parent->isSectionTracker() ) - parent = &parent->parent(); + SectionTracker::SectionTracker(NameAndLocation const &nameAndLocation, TrackerContext &ctx, ITracker *parent) + : TrackerBase(nameAndLocation, ctx, parent) + , m_trimmed_name(trim(nameAndLocation.name)) { + if (parent) { + while (!parent->isSectionTracker()) + parent = &parent->parent(); - SectionTracker& parentSection = static_cast( *parent ); - addNextFilters( parentSection.m_filters ); - } + SectionTracker &parentSection = static_cast(*parent); + addNextFilters(parentSection.m_filters); + } } - bool SectionTracker::isSectionTracker() const { return true; } + bool SectionTracker::isComplete() const { + bool complete = true; - SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { - std::shared_ptr section; + if (m_filters.empty() || m_filters[0] == "" || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) { + complete = TrackerBase::isComplete(); + } + return complete; + } - ITracker& currentTracker = ctx.currentTracker(); - if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isSectionTracker() ); - section = std::static_pointer_cast( childTracker ); - } - else { - section = std::make_shared( nameAndLocation, ctx, ¤tTracker ); - currentTracker.addChild( section ); - } - if( !ctx.completedCycle() ) - section->tryOpen(); - return *section; + bool SectionTracker::isSectionTracker() const { + return true; + } + + SectionTracker &SectionTracker::acquire(TrackerContext &ctx, NameAndLocation const &nameAndLocation) { + std::shared_ptr section; + + ITracker ¤tTracker = ctx.currentTracker(); + if (ITrackerPtr childTracker = currentTracker.findChild(nameAndLocation)) { + assert(childTracker); + assert(childTracker->isSectionTracker()); + section = std::static_pointer_cast(childTracker); + } else { + section = std::make_shared(nameAndLocation, ctx, ¤tTracker); + currentTracker.addChild(section); + } + if (!ctx.completedCycle()) + section->tryOpen(); + return *section; } void SectionTracker::tryOpen() { - if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) - open(); + if (!isComplete()) + open(); } - void SectionTracker::addInitialFilters( std::vector const& filters ) { - if( !filters.empty() ) { - m_filters.push_back(""); // Root - should never be consulted - m_filters.push_back(""); // Test Case - not a section filter - m_filters.insert( m_filters.end(), filters.begin(), filters.end() ); - } + void SectionTracker::addInitialFilters(std::vector const &filters) { + if (!filters.empty()) { + m_filters.reserve(m_filters.size() + filters.size() + 2); + m_filters.emplace_back(""); // Root - should never be consulted + m_filters.emplace_back(""); // Test Case - not a section filter + m_filters.insert(m_filters.end(), filters.begin(), filters.end()); + } } - void SectionTracker::addNextFilters( std::vector const& filters ) { - if( filters.size() > 1 ) - m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() ); + void SectionTracker::addNextFilters(std::vector const &filters) { + if (filters.size() > 1) + m_filters.insert(m_filters.end(), filters.begin() + 1, filters.end()); } - IndexTracker::IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) - : TrackerBase( nameAndLocation, ctx, parent ), - m_size( size ) - {} - - bool IndexTracker::isIndexTracker() const { return true; } - - IndexTracker& IndexTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { - std::shared_ptr tracker; - - ITracker& currentTracker = ctx.currentTracker(); - if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isIndexTracker() ); - tracker = std::static_pointer_cast( childTracker ); - } - else { - tracker = std::make_shared( nameAndLocation, ctx, ¤tTracker, size ); - currentTracker.addChild( tracker ); - } - - if( !ctx.completedCycle() && !tracker->isComplete() ) { - if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) - tracker->moveNext(); - tracker->open(); - } - - return *tracker; + std::vector const &SectionTracker::getFilters() const { + return m_filters; } - int IndexTracker::index() const { return m_index; } - - void IndexTracker::moveNext() { - m_index++; - m_children.clear(); + std::string const &SectionTracker::trimmedName() const { + return m_trimmed_name; } - void IndexTracker::close() { - TrackerBase::close(); - if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) - m_runState = Executing; - } + } // namespace TestCaseTracking -} // namespace TestCaseTracking - -using TestCaseTracking::ITracker; -using TestCaseTracking::TrackerContext; -using TestCaseTracking::SectionTracker; -using TestCaseTracking::IndexTracker; + using TestCaseTracking::ITracker; + using TestCaseTracking::SectionTracker; + using TestCaseTracking::TrackerContext; } // namespace Catch #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif // end catch_test_case_tracker.cpp // start catch_test_registry.cpp namespace Catch { + auto makeTestInvoker(void (*testAsFunction)()) noexcept -> ITestInvoker * { + return new (std::nothrow) TestInvokerAsFunction(testAsFunction); + } - auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* { - return new(std::nothrow) TestInvokerAsFunction( testAsFunction ); + NameAndTags::NameAndTags(StringRef const &name_, StringRef const &tags_) noexcept + : name(name_) + , tags(tags_) { + } + + AutoReg::AutoReg(ITestInvoker *invoker, SourceLineInfo const &lineInfo, StringRef const &classOrMethod, NameAndTags const &nameAndTags) noexcept { + CATCH_TRY { getMutableRegistryHub().registerTest(makeTestCase(invoker, extractClassName(classOrMethod), nameAndTags, lineInfo)); } + CATCH_CATCH_ALL { + // Do not throw when constructing global objects, instead register the exception to be processed later + getMutableRegistryHub().registerStartupException(); } + } - NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {} - - AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept { - try { - getMutableRegistryHub() - .registerTest( - makeTestCase( - invoker, - extractClassName( classOrMethod ), - nameAndTags, - lineInfo)); - } catch (...) { - // Do not throw when constructing global objects, instead register the exception to be processed later - getMutableRegistryHub().registerStartupException(); - } - } - - AutoReg::~AutoReg() = default; -} + AutoReg::~AutoReg() = default; +} // namespace Catch // end catch_test_registry.cpp // start catch_test_spec.cpp #include +#include #include #include -#include namespace Catch { + TestSpec::Pattern::Pattern(std::string const &name) + : m_name(name) { + } - TestSpec::Pattern::~Pattern() = default; - TestSpec::NamePattern::~NamePattern() = default; - TestSpec::TagPattern::~TagPattern() = default; - TestSpec::ExcludedPattern::~ExcludedPattern() = default; + TestSpec::Pattern::~Pattern() = default; - TestSpec::NamePattern::NamePattern( std::string const& name ) - : m_wildcardPattern( toLower( name ), CaseSensitive::No ) - {} - bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const { - return m_wildcardPattern.matches( toLower( testCase.name ) ); - } + std::string const &TestSpec::Pattern::name() const { + return m_name; + } - TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} - bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const { - return std::find(begin(testCase.lcaseTags), - end(testCase.lcaseTags), - m_tag) != end(testCase.lcaseTags); - } + TestSpec::NamePattern::NamePattern(std::string const &name, std::string const &filterString) + : Pattern(filterString) + , m_wildcardPattern(toLower(name), CaseSensitive::No) { + } - TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} - bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + bool TestSpec::NamePattern::matches(TestCaseInfo const &testCase) const { + return m_wildcardPattern.matches(testCase.name); + } - bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const { - // All patterns in a filter must match for the filter to be a match - for( auto const& pattern : m_patterns ) { - if( !pattern->matches( testCase ) ) - return false; - } - return true; - } + TestSpec::TagPattern::TagPattern(std::string const &tag, std::string const &filterString) + : Pattern(filterString) + , m_tag(toLower(tag)) { + } - bool TestSpec::hasFilters() const { - return !m_filters.empty(); - } - bool TestSpec::matches( TestCaseInfo const& testCase ) const { - // A TestSpec matches if any filter matches - for( auto const& filter : m_filters ) - if( filter.matches( testCase ) ) - return true; - return false; - } -} + bool TestSpec::TagPattern::matches(TestCaseInfo const &testCase) const { + return std::find(begin(testCase.lcaseTags), end(testCase.lcaseTags), m_tag) != end(testCase.lcaseTags); + } + + TestSpec::ExcludedPattern::ExcludedPattern(PatternPtr const &underlyingPattern) + : Pattern(underlyingPattern->name()) + , m_underlyingPattern(underlyingPattern) { + } + + bool TestSpec::ExcludedPattern::matches(TestCaseInfo const &testCase) const { + return !m_underlyingPattern->matches(testCase); + } + + bool TestSpec::Filter::matches(TestCaseInfo const &testCase) const { + return std::all_of(m_patterns.begin(), m_patterns.end(), [&](PatternPtr const &p) { return p->matches(testCase); }); + } + + std::string TestSpec::Filter::name() const { + std::string name; + for (auto const &p : m_patterns) + name += p->name(); + return name; + } + + bool TestSpec::hasFilters() const { + return !m_filters.empty(); + } + + bool TestSpec::matches(TestCaseInfo const &testCase) const { + return std::any_of(m_filters.begin(), m_filters.end(), [&](Filter const &f) { return f.matches(testCase); }); + } + + TestSpec::Matches TestSpec::matchesByFilter(std::vector const &testCases, IConfig const &config) const { + Matches matches(m_filters.size()); + std::transform(m_filters.begin(), m_filters.end(), matches.begin(), [&](Filter const &filter) { + std::vector currentMatches; + for (auto const &test : testCases) + if (isThrowSafe(test, config) && filter.matches(test)) + currentMatches.emplace_back(&test); + return FilterMatch{filter.name(), currentMatches}; + }); + return matches; + } + + const TestSpec::vectorStrings &TestSpec::getInvalidArgs() const { + return (m_invalidArgs); + } + +} // namespace Catch // end catch_test_spec.cpp // start catch_test_spec_parser.cpp namespace Catch { + TestSpecParser::TestSpecParser(ITagAliasRegistry const &tagAliases) + : m_tagAliases(&tagAliases) { + } - TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + TestSpecParser &TestSpecParser::parse(std::string const &arg) { + m_mode = None; + m_exclusion = false; + m_arg = m_tagAliases->expandAliases(arg); + m_escapeChars.clear(); + m_substring.reserve(m_arg.size()); + m_patternName.reserve(m_arg.size()); + m_realPatternPos = 0; - TestSpecParser& TestSpecParser::parse( std::string const& arg ) { - m_mode = None; - m_exclusion = false; - m_start = std::string::npos; - m_arg = m_tagAliases->expandAliases( arg ); - m_escapeChars.clear(); - for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) - visitChar( m_arg[m_pos] ); - if( m_mode == Name ) - addPattern(); - return *this; - } - TestSpec TestSpecParser::testSpec() { - addFilter(); - return m_testSpec; + for (m_pos = 0; m_pos < m_arg.size(); ++m_pos) + // if visitChar fails + if (!visitChar(m_arg[m_pos])) { + m_testSpec.m_invalidArgs.push_back(arg); + break; + } + endMode(); + return *this; + } + TestSpec TestSpecParser::testSpec() { + addFilter(); + return m_testSpec; + } + bool TestSpecParser::visitChar(char c) { + if ((m_mode != EscapedName) && (c == '\\')) { + escape(); + addCharToPattern(c); + return true; + } else if ((m_mode != EscapedName) && (c == ',')) { + return separate(); } - void TestSpecParser::visitChar( char c ) { - if( m_mode == None ) { - switch( c ) { - case ' ': return; - case '~': m_exclusion = true; return; - case '[': return startNewMode( Tag, ++m_pos ); - case '"': return startNewMode( QuotedName, ++m_pos ); - case '\\': return escape(); - default: startNewMode( Name, m_pos ); break; - } + switch (m_mode) { + case None: + if (processNoneChar(c)) + return true; + break; + case Name: + processNameChar(c); + break; + case EscapedName: + endMode(); + addCharToPattern(c); + return true; + default: + case Tag: + case QuotedName: + if (processOtherChar(c)) + return true; + break; + } + + m_substring += c; + if (!isControlChar(c)) { + m_patternName += c; + m_realPatternPos++; + } + return true; + } + // Two of the processing methods return true to signal the caller to return + // without adding the given character to the current pattern strings + bool TestSpecParser::processNoneChar(char c) { + switch (c) { + case ' ': + return true; + case '~': + m_exclusion = true; + return false; + case '[': + startNewMode(Tag); + return false; + case '"': + startNewMode(QuotedName); + return false; + default: + startNewMode(Name); + return false; + } + } + void TestSpecParser::processNameChar(char c) { + if (c == '[') { + if (m_substring == "exclude:") + m_exclusion = true; + else + endMode(); + startNewMode(Tag); + } + } + bool TestSpecParser::processOtherChar(char c) { + if (!isControlChar(c)) + return false; + m_substring += c; + endMode(); + return true; + } + void TestSpecParser::startNewMode(Mode mode) { + m_mode = mode; + } + void TestSpecParser::endMode() { + switch (m_mode) { + case Name: + case QuotedName: + return addNamePattern(); + case Tag: + return addTagPattern(); + case EscapedName: + revertBackToLastMode(); + return; + case None: + default: + return startNewMode(None); + } + } + void TestSpecParser::escape() { + saveLastMode(); + m_mode = EscapedName; + m_escapeChars.push_back(m_realPatternPos); + } + bool TestSpecParser::isControlChar(char c) const { + switch (m_mode) { + default: + return false; + case None: + return c == '~'; + case Name: + return c == '['; + case EscapedName: + return true; + case QuotedName: + return c == '"'; + case Tag: + return c == '[' || c == ']'; + } + } + + void TestSpecParser::addFilter() { + if (!m_currentFilter.m_patterns.empty()) { + m_testSpec.m_filters.push_back(m_currentFilter); + m_currentFilter = TestSpec::Filter(); + } + } + + void TestSpecParser::saveLastMode() { + lastMode = m_mode; + } + + void TestSpecParser::revertBackToLastMode() { + m_mode = lastMode; + } + + bool TestSpecParser::separate() { + if ((m_mode == QuotedName) || (m_mode == Tag)) { + // invalid argument, signal failure to previous scope. + m_mode = None; + m_pos = m_arg.size(); + m_substring.clear(); + m_patternName.clear(); + m_realPatternPos = 0; + return false; + } + endMode(); + addFilter(); + return true; // success + } + + std::string TestSpecParser::preprocessPattern() { + std::string token = m_patternName; + for (std::size_t i = 0; i < m_escapeChars.size(); ++i) + token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1); + m_escapeChars.clear(); + if (startsWith(token, "exclude:")) { + m_exclusion = true; + token = token.substr(8); + } + + m_patternName.clear(); + m_realPatternPos = 0; + + return token; + } + + void TestSpecParser::addNamePattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); + if (m_exclusion) + pattern = std::make_shared(pattern); + m_currentFilter.m_patterns.push_back(pattern); + } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + void TestSpecParser::addTagPattern() { + auto token = preprocessPattern(); + + if (!token.empty()) { + // If the tag pattern is the "hide and tag" shorthand (e.g. [.foo]) + // we have to create a separate hide tag and shorten the real one + if (token.size() > 1 && token[0] == '.') { + token.erase(token.begin()); + TestSpec::PatternPtr pattern = std::make_shared(".", m_substring); + if (m_exclusion) { + pattern = std::make_shared(pattern); } - if( m_mode == Name ) { - if( c == ',' ) { - addPattern(); - addFilter(); - } - else if( c == '[' ) { - if( subString() == "exclude:" ) - m_exclusion = true; - else - addPattern(); - startNewMode( Tag, ++m_pos ); - } - else if( c == '\\' ) - escape(); - } - else if( m_mode == EscapedName ) - m_mode = Name; - else if( m_mode == QuotedName && c == '"' ) - addPattern(); - else if( m_mode == Tag && c == ']' ) - addPattern(); - } - void TestSpecParser::startNewMode( Mode mode, std::size_t start ) { - m_mode = mode; - m_start = start; - } - void TestSpecParser::escape() { - if( m_mode == None ) - m_start = m_pos; - m_mode = EscapedName; - m_escapeChars.push_back( m_pos ); - } - std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + m_currentFilter.m_patterns.push_back(pattern); + } - void TestSpecParser::addFilter() { - if( !m_currentFilter.m_patterns.empty() ) { - m_testSpec.m_filters.push_back( m_currentFilter ); - m_currentFilter = TestSpec::Filter(); - } - } + TestSpec::PatternPtr pattern = std::make_shared(token, m_substring); - TestSpec parseTestSpec( std::string const& arg ) { - return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + if (m_exclusion) { + pattern = std::make_shared(pattern); + } + m_currentFilter.m_patterns.push_back(pattern); } + m_substring.clear(); + m_exclusion = false; + m_mode = None; + } + + TestSpec parseTestSpec(std::string const &arg) { + return TestSpecParser(ITagAliasRegistry::get()).parse(arg).testSpec(); + } } // namespace Catch // end catch_test_spec_parser.cpp @@ -10524,69 +15048,69 @@ namespace Catch { static const uint64_t nanosecondsInSecond = 1000000000; namespace Catch { + auto getCurrentNanosecondsSinceEpoch() -> uint64_t { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); + } - auto getCurrentNanosecondsSinceEpoch() -> uint64_t { - return std::chrono::duration_cast( std::chrono::high_resolution_clock::now().time_since_epoch() ).count(); - } - + namespace { auto estimateClockResolution() -> uint64_t { - uint64_t sum = 0; - static const uint64_t iterations = 1000000; + uint64_t sum = 0; + static const uint64_t iterations = 1000000; - auto startTime = getCurrentNanosecondsSinceEpoch(); + auto startTime = getCurrentNanosecondsSinceEpoch(); - for( std::size_t i = 0; i < iterations; ++i ) { + for (std::size_t i = 0; i < iterations; ++i) { + uint64_t ticks; + uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); + do { + ticks = getCurrentNanosecondsSinceEpoch(); + } while (ticks == baseTicks); - uint64_t ticks; - uint64_t baseTicks = getCurrentNanosecondsSinceEpoch(); - do { - ticks = getCurrentNanosecondsSinceEpoch(); - } while( ticks == baseTicks ); + auto delta = ticks - baseTicks; + sum += delta; - auto delta = ticks - baseTicks; - sum += delta; - - // If we have been calibrating for over 3 seconds -- the clock - // is terrible and we should move on. - // TBD: How to signal that the measured resolution is probably wrong? - if (ticks > startTime + 3 * nanosecondsInSecond) { - return sum / i; - } + // If we have been calibrating for over 3 seconds -- the clock + // is terrible and we should move on. + // TBD: How to signal that the measured resolution is probably wrong? + if (ticks > startTime + 3 * nanosecondsInSecond) { + return sum / (i + 1u); } + } - // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers - // - and potentially do more iterations if there's a high variance. - return sum/iterations; - } - auto getEstimatedClockResolution() -> uint64_t { - static auto s_resolution = estimateClockResolution(); - return s_resolution; + // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers + // - and potentially do more iterations if there's a high variance. + return sum / iterations; } + } // namespace + auto getEstimatedClockResolution() -> uint64_t { + static auto s_resolution = estimateClockResolution(); + return s_resolution; + } - void Timer::start() { - m_nanoseconds = getCurrentNanosecondsSinceEpoch(); - } - auto Timer::getElapsedNanoseconds() const -> uint64_t { - return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; - } - auto Timer::getElapsedMicroseconds() const -> uint64_t { - return getElapsedNanoseconds()/1000; - } - auto Timer::getElapsedMilliseconds() const -> unsigned int { - return static_cast(getElapsedMicroseconds()/1000); - } - auto Timer::getElapsedSeconds() const -> double { - return getElapsedMicroseconds()/1000000.0; - } + void Timer::start() { + m_nanoseconds = getCurrentNanosecondsSinceEpoch(); + } + auto Timer::getElapsedNanoseconds() const -> uint64_t { + return getCurrentNanosecondsSinceEpoch() - m_nanoseconds; + } + auto Timer::getElapsedMicroseconds() const -> uint64_t { + return getElapsedNanoseconds() / 1000; + } + auto Timer::getElapsedMilliseconds() const -> unsigned int { + return static_cast(getElapsedMicroseconds() / 1000); + } + auto Timer::getElapsedSeconds() const -> double { + return getElapsedMicroseconds() / 1000000.0; + } } // namespace Catch // end catch_timer.cpp // start catch_tostring.cpp #if defined(__clang__) -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wexit-time-destructors" -# pragma clang diagnostic ignored "-Wglobal-constructors" +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wglobal-constructors" #endif // Enable specific decls locally @@ -10598,285 +15122,351 @@ namespace Catch { #include namespace Catch { - -namespace Detail { - + namespace Detail { const std::string unprintableString = "{?}"; namespace { - const int hexThreshold = 255; + const int hexThreshold = 255; - struct Endianness { - enum Arch { Big, Little }; - - static Arch which() { - union _{ - int asInt; - char asChar[sizeof (int)]; - } u; - - u.asInt = 1; - return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; - } + struct Endianness { + enum Arch { + Big, + Little }; - } - std::string rawMemoryToString( const void *object, std::size_t size ) { - // Reverse order for little endian architectures - int i = 0, end = static_cast( size ), inc = 1; - if( Endianness::which() == Endianness::Little ) { - i = end-1; - end = inc = -1; + static Arch which() { + int one = 1; + // If the lowest byte we read is non-zero, we can assume + // that little endian format is used. + auto value = *reinterpret_cast(&one); + return value ? Little : Big; } + }; + } // namespace - unsigned char const *bytes = static_cast(object); - ReusableStringStream rss; - rss << "0x" << std::setfill('0') << std::hex; - for( ; i != end; i += inc ) - rss << std::setw(2) << static_cast(bytes[i]); - return rss.str(); + std::string rawMemoryToString(const void *object, std::size_t size) { + // Reverse order for little endian architectures + int i = 0, end = static_cast(size), inc = 1; + if (Endianness::which() == Endianness::Little) { + i = end - 1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + ReusableStringStream rss; + rss << "0x" << std::setfill('0') << std::hex; + for (; i != end; i += inc) + rss << std::setw(2) << static_cast(bytes[i]); + return rss.str(); } -} + } // namespace Detail -template -std::string fpToString( T value, int precision ) { - if (std::isnan(value)) { - return "nan"; + template + std::string fpToString(T value, int precision) { + if (Catch::isnan(value)) { + return "nan"; } ReusableStringStream rss; - rss << std::setprecision( precision ) - << std::fixed - << value; + rss << std::setprecision(precision) << std::fixed << value; std::string d = rss.str(); - std::size_t i = d.find_last_not_of( '0' ); - if( i != std::string::npos && i != d.size()-1 ) { - if( d[i] == '.' ) - i++; - d = d.substr( 0, i+1 ); + std::size_t i = d.find_last_not_of('0'); + if (i != std::string::npos && i != d.size() - 1) { + if (d[i] == '.') + i++; + d = d.substr(0, i + 1); } return d; -} + } -//// ======================================================= //// -// -// Out-of-line defs for full specialization of StringMaker -// -//// ======================================================= //// + //// ======================================================= //// + // + // Out-of-line defs for full specialization of StringMaker + // + //// ======================================================= //// -std::string StringMaker::convert(const std::string& str) { + std::string StringMaker::convert(const std::string &str) { if (!getCurrentContext().getConfig()->showInvisibles()) { - return '"' + str + '"'; + return '"' + str + '"'; } std::string s("\""); for (char c : str) { - switch (c) { + switch (c) { case '\n': - s.append("\\n"); - break; + s.append("\\n"); + break; case '\t': - s.append("\\t"); - break; + s.append("\\t"); + break; default: - s.push_back(c); - break; - } + s.push_back(c); + break; + } } s.append("\""); return s; -} + } + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + std::string StringMaker::convert(std::string_view str) { + return ::Catch::Detail::stringify(std::string{str}); + } +#endif + + std::string StringMaker::convert(char const *str) { + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } + } + std::string StringMaker::convert(char *str) { + if (str) { + return ::Catch::Detail::stringify(std::string{str}); + } else { + return {"{null string}"}; + } + } #ifdef CATCH_CONFIG_WCHAR -std::string StringMaker::convert(const std::wstring& wstr) { + std::string StringMaker::convert(const std::wstring &wstr) { std::string s; s.reserve(wstr.size()); for (auto c : wstr) { - s += (c <= 0xff) ? static_cast(c) : '?'; + s += (c <= 0xff) ? static_cast(c) : '?'; } return ::Catch::Detail::stringify(s); -} + } + +#ifdef CATCH_CONFIG_CPP17_STRING_VIEW + std::string StringMaker::convert(std::wstring_view str) { + return StringMaker::convert(std::wstring(str)); + } #endif -std::string StringMaker::convert(char const* str) { + std::string StringMaker::convert(wchar_t const *str) { if (str) { - return ::Catch::Detail::stringify(std::string{ str }); + return ::Catch::Detail::stringify(std::wstring{str}); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} -std::string StringMaker::convert(char* str) { + } + std::string StringMaker::convert(wchar_t *str) { if (str) { - return ::Catch::Detail::stringify(std::string{ str }); + return ::Catch::Detail::stringify(std::wstring{str}); } else { - return{ "{null string}" }; + return {"{null string}"}; } -} -#ifdef CATCH_CONFIG_WCHAR -std::string StringMaker::convert(wchar_t const * str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); - } else { - return{ "{null string}" }; - } -} -std::string StringMaker::convert(wchar_t * str) { - if (str) { - return ::Catch::Detail::stringify(std::wstring{ str }); - } else { - return{ "{null string}" }; - } -} + } #endif -std::string StringMaker::convert(int value) { +#if defined(CATCH_CONFIG_CPP17_BYTE) +#include + std::string StringMaker::convert(std::byte value) { + return ::Catch::Detail::stringify(std::to_integer(value)); + } +#endif // defined(CATCH_CONFIG_CPP17_BYTE) + + std::string StringMaker::convert(int value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long value) { + } + std::string StringMaker::convert(long value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(long long value) { + } + std::string StringMaker::convert(long long value) { ReusableStringStream rss; rss << value; if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; + rss << " (0x" << std::hex << value << ')'; } return rss.str(); -} + } -std::string StringMaker::convert(unsigned int value) { + std::string StringMaker::convert(unsigned int value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long value) { + } + std::string StringMaker::convert(unsigned long value) { return ::Catch::Detail::stringify(static_cast(value)); -} -std::string StringMaker::convert(unsigned long long value) { + } + std::string StringMaker::convert(unsigned long long value) { ReusableStringStream rss; rss << value; if (value > Detail::hexThreshold) { - rss << " (0x" << std::hex << value << ')'; + rss << " (0x" << std::hex << value << ')'; } return rss.str(); -} + } -std::string StringMaker::convert(bool b) { + std::string StringMaker::convert(bool b) { return b ? "true" : "false"; -} + } -std::string StringMaker::convert(char value) { + std::string StringMaker::convert(signed char value) { if (value == '\r') { - return "'\\r'"; + return "'\\r'"; } else if (value == '\f') { - return "'\\f'"; + return "'\\f'"; } else if (value == '\n') { - return "'\\n'"; + return "'\\n'"; } else if (value == '\t') { - return "'\\t'"; + return "'\\t'"; } else if ('\0' <= value && value < ' ') { - return ::Catch::Detail::stringify(static_cast(value)); + return ::Catch::Detail::stringify(static_cast(value)); } else { - char chstr[] = "' '"; - chstr[1] = value; - return chstr; + char chstr[] = "' '"; + chstr[1] = value; + return chstr; } -} -std::string StringMaker::convert(signed char c) { + } + std::string StringMaker::convert(char c) { + return ::Catch::Detail::stringify(static_cast(c)); + } + std::string StringMaker::convert(unsigned char c) { return ::Catch::Detail::stringify(static_cast(c)); -} -std::string StringMaker::convert(unsigned char c) { - return ::Catch::Detail::stringify(static_cast(c)); -} + } -std::string StringMaker::convert(std::nullptr_t) { + std::string StringMaker::convert(std::nullptr_t) { return "nullptr"; -} + } -std::string StringMaker::convert(float value) { - return fpToString(value, 5) + 'f'; -} -std::string StringMaker::convert(double value) { - return fpToString(value, 10); -} + int StringMaker::precision = 5; -std::string ratio_string::symbol() { return "a"; } -std::string ratio_string::symbol() { return "f"; } -std::string ratio_string::symbol() { return "p"; } -std::string ratio_string::symbol() { return "n"; } -std::string ratio_string::symbol() { return "u"; } -std::string ratio_string::symbol() { return "m"; } + std::string StringMaker::convert(float value) { + return fpToString(value, precision) + 'f'; + } + + int StringMaker::precision = 10; + + std::string StringMaker::convert(double value) { + return fpToString(value, precision); + } + + std::string ratio_string::symbol() { + return "a"; + } + std::string ratio_string::symbol() { + return "f"; + } + std::string ratio_string::symbol() { + return "p"; + } + std::string ratio_string::symbol() { + return "n"; + } + std::string ratio_string::symbol() { + return "u"; + } + std::string ratio_string::symbol() { + return "m"; + } } // end namespace Catch #if defined(__clang__) -# pragma clang diagnostic pop +#pragma clang diagnostic pop #endif // end catch_tostring.cpp // start catch_totals.cpp namespace Catch { + Counts Counts::operator-(Counts const &other) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } - Counts Counts::operator - ( Counts const& other ) const { - Counts diff; - diff.passed = passed - other.passed; - diff.failed = failed - other.failed; - diff.failedButOk = failedButOk - other.failedButOk; - return diff; - } + Counts &Counts::operator+=(Counts const &other) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } - Counts& Counts::operator += ( Counts const& other ) { - passed += other.passed; - failed += other.failed; - failedButOk += other.failedButOk; - return *this; - } + std::size_t Counts::total() const { + return passed + failed + failedButOk; + } + bool Counts::allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool Counts::allOk() const { + return failed == 0; + } - std::size_t Counts::total() const { - return passed + failed + failedButOk; - } - bool Counts::allPassed() const { - return failed == 0 && failedButOk == 0; - } - bool Counts::allOk() const { - return failed == 0; - } + Totals Totals::operator-(Totals const &other) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } - Totals Totals::operator - ( Totals const& other ) const { - Totals diff; - diff.assertions = assertions - other.assertions; - diff.testCases = testCases - other.testCases; - return diff; - } + Totals &Totals::operator+=(Totals const &other) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } - Totals& Totals::operator += ( Totals const& other ) { - assertions += other.assertions; - testCases += other.testCases; - return *this; - } + Totals Totals::delta(Totals const &prevTotals) const { + Totals diff = *this - prevTotals; + if (diff.assertions.failed > 0) + ++diff.testCases.failed; + else if (diff.assertions.failedButOk > 0) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } - Totals Totals::delta( Totals const& prevTotals ) const { - Totals diff = *this - prevTotals; - if( diff.assertions.failed > 0 ) - ++diff.testCases.failed; - else if( diff.assertions.failedButOk > 0 ) - ++diff.testCases.failedButOk; - else - ++diff.testCases.passed; - return diff; - } - -} +} // namespace Catch // end catch_totals.cpp // start catch_uncaught_exceptions.cpp +// start catch_config_uncaught_exceptions.hpp + +// Copyright Catch2 Authors +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// https://www.boost.org/LICENSE_1_0.txt) + +// SPDX-License-Identifier: BSL-1.0 + +#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP +#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP + +#if defined(_MSC_VER) +#if _MSC_VER >= 1900 // Visual Studio 2015 or newer +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif +#endif + +#include + +#if defined(__cpp_lib_uncaught_exceptions) && !defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif // __cpp_lib_uncaught_exceptions + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) \ + && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + +#define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS +#endif + +#endif // CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP \ + // end catch_config_uncaught_exceptions.hpp #include namespace Catch { - bool uncaught_exceptions() { -#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) - return std::uncaught_exceptions() > 0; + bool uncaught_exceptions() { +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) + return false; +#elif defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) + return std::uncaught_exceptions() > 0; #else - return std::uncaught_exception(); + return std::uncaught_exception(); #endif } } // end namespace Catch @@ -10886,680 +15476,735 @@ namespace Catch { #include namespace Catch { + Version::Version(unsigned int _majorVersion, unsigned int _minorVersion, unsigned int _patchNumber, char const *const _branchName, unsigned int _buildNumber) + : majorVersion(_majorVersion) + , minorVersion(_minorVersion) + , patchNumber(_patchNumber) + , branchName(_branchName) + , buildNumber(_buildNumber) { + } - Version::Version - ( unsigned int _majorVersion, - unsigned int _minorVersion, - unsigned int _patchNumber, - char const * const _branchName, - unsigned int _buildNumber ) - : majorVersion( _majorVersion ), - minorVersion( _minorVersion ), - patchNumber( _patchNumber ), - branchName( _branchName ), - buildNumber( _buildNumber ) - {} - - std::ostream& operator << ( std::ostream& os, Version const& version ) { - os << version.majorVersion << '.' - << version.minorVersion << '.' - << version.patchNumber; - // branchName is never null -> 0th char is \0 if it is empty - if (version.branchName[0]) { - os << '-' << version.branchName - << '.' << version.buildNumber; - } - return os; + std::ostream &operator<<(std::ostream &os, Version const &version) { + os << version.majorVersion << '.' << version.minorVersion << '.' << version.patchNumber; + // branchName is never null -> 0th char is \0 if it is empty + if (version.branchName[0]) { + os << '-' << version.branchName << '.' << version.buildNumber; } + return os; + } - Version const& libraryVersion() { - static Version version( 2, 2, 2, "", 0 ); - return version; - } + Version const &libraryVersion() { + static Version version(2, 13, 6, "", 0); + return version; + } -} +} // namespace Catch // end catch_version.cpp // start catch_wildcard_pattern.cpp -#include - namespace Catch { - - WildcardPattern::WildcardPattern( std::string const& pattern, - CaseSensitive::Choice caseSensitivity ) - : m_caseSensitivity( caseSensitivity ), - m_pattern( adjustCase( pattern ) ) - { - if( startsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 1 ); - m_wildcard = WildcardAtStart; - } - if( endsWith( m_pattern, '*' ) ) { - m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); - m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); - } + WildcardPattern::WildcardPattern(std::string const &pattern, CaseSensitive::Choice caseSensitivity) + : m_caseSensitivity(caseSensitivity) + , m_pattern(normaliseString(pattern)) { + if (startsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(1); + m_wildcard = WildcardAtStart; } - - bool WildcardPattern::matches( std::string const& str ) const { - switch( m_wildcard ) { - case NoWildcard: - return m_pattern == adjustCase( str ); - case WildcardAtStart: - return endsWith( adjustCase( str ), m_pattern ); - case WildcardAtEnd: - return startsWith( adjustCase( str ), m_pattern ); - case WildcardAtBothEnds: - return contains( adjustCase( str ), m_pattern ); - default: - CATCH_INTERNAL_ERROR( "Unknown enum" ); - } + if (endsWith(m_pattern, '*')) { + m_pattern = m_pattern.substr(0, m_pattern.size() - 1); + m_wildcard = static_cast(m_wildcard | WildcardAtEnd); } + } - std::string WildcardPattern::adjustCase( std::string const& str ) const { - return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + bool WildcardPattern::matches(std::string const &str) const { + switch (m_wildcard) { + case NoWildcard: + return m_pattern == normaliseString(str); + case WildcardAtStart: + return endsWith(normaliseString(str), m_pattern); + case WildcardAtEnd: + return startsWith(normaliseString(str), m_pattern); + case WildcardAtBothEnds: + return contains(normaliseString(str), m_pattern); + default: + CATCH_INTERNAL_ERROR("Unknown enum"); } -} + } + + std::string WildcardPattern::normaliseString(std::string const &str) const { + return trim(m_caseSensitivity == CaseSensitive::No ? toLower(str) : str); + } +} // namespace Catch // end catch_wildcard_pattern.cpp // start catch_xmlwriter.cpp #include - -using uchar = unsigned char; +#include namespace Catch { - -namespace { - + namespace { size_t trailingBytes(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return 2; - } - if ((c & 0xF0) == 0xE0) { - return 3; - } - if ((c & 0xF8) == 0xF0) { - return 4; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + if ((c & 0xE0) == 0xC0) { + return 2; + } + if ((c & 0xF0) == 0xE0) { + return 3; + } + if ((c & 0xF8) == 0xF0) { + return 4; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } uint32_t headerValue(unsigned char c) { - if ((c & 0xE0) == 0xC0) { - return c & 0x1F; - } - if ((c & 0xF0) == 0xE0) { - return c & 0x0F; - } - if ((c & 0xF8) == 0xF0) { - return c & 0x07; - } - CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); + if ((c & 0xE0) == 0xC0) { + return c & 0x1F; + } + if ((c & 0xF0) == 0xE0) { + return c & 0x0F; + } + if ((c & 0xF8) == 0xF0) { + return c & 0x07; + } + CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered"); } - void hexEscapeChar(std::ostream& os, unsigned char c) { - os << "\\x" - << std::uppercase << std::hex << std::setfill('0') << std::setw(2) - << static_cast(c); + void hexEscapeChar(std::ostream &os, unsigned char c) { + std::ios_base::fmtflags f(os.flags()); + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) << static_cast(c); + os.flags(f); } -} // anonymous namespace - - XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat ) - : m_str( str ), - m_forWhat( forWhat ) - {} - - void XmlEncode::encodeTo( std::ostream& os ) const { - // Apostrophe escaping not necessary if we always use " to write attributes - // (see: http://www.w3.org/TR/xml/#syntax) - - for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) { - uchar c = m_str[idx]; - switch (c) { - case '<': os << "<"; break; - case '&': os << "&"; break; - - case '>': - // See: http://www.w3.org/TR/xml/#syntax - if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') - os << ">"; - else - os << c; - break; - - case '\"': - if (m_forWhat == ForAttributes) - os << """; - else - os << c; - break; - - default: - // Check for control characters and invalid utf-8 - - // Escape control characters in standard ascii - // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 - if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { - hexEscapeChar(os, c); - break; - } - - // Plain ASCII: Write it to stream - if (c < 0x7F) { - os << c; - break; - } - - // UTF-8 territory - // Check if the encoding is valid and if it is not, hex escape bytes. - // Important: We do not check the exact decoded values for validity, only the encoding format - // First check that this bytes is a valid lead byte: - // This means that it is not encoded as 1111 1XXX - // Or as 10XX XXXX - if (c < 0xC0 || - c >= 0xF8) { - hexEscapeChar(os, c); - break; - } - - auto encBytes = trailingBytes(c); - // Are there enough bytes left to avoid accessing out-of-bounds memory? - if (idx + encBytes - 1 >= m_str.size()) { - hexEscapeChar(os, c); - break; - } - // The header is valid, check data - // The next encBytes bytes must together be a valid utf-8 - // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) - bool valid = true; - uint32_t value = headerValue(c); - for (std::size_t n = 1; n < encBytes; ++n) { - uchar nc = m_str[idx + n]; - valid &= ((nc & 0xC0) == 0x80); - value = (value << 6) | (nc & 0x3F); - } - - if ( - // Wrong bit pattern of following bytes - (!valid) || - // Overlong encodings - (value < 0x80) || - (0x80 <= value && value < 0x800 && encBytes > 2) || - (0x800 < value && value < 0x10000 && encBytes > 3) || - // Encoded value out of range - (value >= 0x110000) - ) { - hexEscapeChar(os, c); - break; - } - - // If we got here, this is in fact a valid(ish) utf-8 sequence - for (std::size_t n = 0; n < encBytes; ++n) { - os << m_str[idx + n]; - } - idx += encBytes - 1; - break; - } - } + bool shouldNewline(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Newline)); } - std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { - xmlEncode.encodeTo( os ); - return os; + bool shouldIndent(XmlFormatting fmt) { + return !!(static_cast::type>(fmt & XmlFormatting::Indent)); } - XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer ) - : m_writer( writer ) - {} + } // anonymous namespace - XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept - : m_writer( other.m_writer ){ - other.m_writer = nullptr; - } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept { - if ( m_writer ) { - m_writer->endElement(); - } - m_writer = other.m_writer; - other.m_writer = nullptr; - return *this; - } + XmlFormatting operator|(XmlFormatting lhs, XmlFormatting rhs) { + return static_cast(static_cast::type>(lhs) + | static_cast::type>(rhs)); + } - XmlWriter::ScopedElement::~ScopedElement() { - if( m_writer ) - m_writer->endElement(); - } + XmlFormatting operator&(XmlFormatting lhs, XmlFormatting rhs) { + return static_cast(static_cast::type>(lhs) + & static_cast::type>(rhs)); + } - XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) { - m_writer->writeText( text, indent ); - return *this; - } + XmlEncode::XmlEncode(std::string const &str, ForWhat forWhat) + : m_str(str) + , m_forWhat(forWhat) { + } - XmlWriter::XmlWriter( std::ostream& os ) : m_os( os ) - { - writeDeclaration(); - } + void XmlEncode::encodeTo(std::ostream &os) const { + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) - XmlWriter::~XmlWriter() { - while( !m_tags.empty() ) - endElement(); - } + for (std::size_t idx = 0; idx < m_str.size(); ++idx) { + unsigned char c = m_str[idx]; + switch (c) { + case '<': + os << "<"; + break; + case '&': + os << "&"; + break; - XmlWriter& XmlWriter::startElement( std::string const& name ) { - ensureTagClosed(); - newlineIfNecessary(); - m_os << m_indent << '<' << name; - m_tags.push_back( name ); - m_indent += " "; - m_tagIsOpen = true; - return *this; - } + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']') + os << ">"; + else + os << c; + break; - XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) { - ScopedElement scoped( this ); - startElement( name ); - return scoped; - } + case '\"': + if (m_forWhat == ForAttributes) + os << """; + else + os << c; + break; - XmlWriter& XmlWriter::endElement() { - newlineIfNecessary(); - m_indent = m_indent.substr( 0, m_indent.size()-2 ); - if( m_tagIsOpen ) { - m_os << "/>"; - m_tagIsOpen = false; - } - else { - m_os << m_indent << ""; - } - m_os << std::endl; - m_tags.pop_back(); - return *this; - } + default: + // Check for control characters and invalid utf-8 - XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) { - if( !name.empty() && !attribute.empty() ) - m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; - return *this; - } + // Escape control characters in standard ascii + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) { + hexEscapeChar(os, c); + break; + } - XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) { - m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; - return *this; - } + // Plain ASCII: Write it to stream + if (c < 0x7F) { + os << c; + break; + } - XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) { - if( !text.empty() ){ - bool tagWasOpen = m_tagIsOpen; - ensureTagClosed(); - if( tagWasOpen && indent ) - m_os << m_indent; - m_os << XmlEncode( text ); - m_needsNewline = true; - } - return *this; - } + // UTF-8 territory + // Check if the encoding is valid and if it is not, hex escape bytes. + // Important: We do not check the exact decoded values for validity, only the encoding format + // First check that this bytes is a valid lead byte: + // This means that it is not encoded as 1111 1XXX + // Or as 10XX XXXX + if (c < 0xC0 || c >= 0xF8) { + hexEscapeChar(os, c); + break; + } - XmlWriter& XmlWriter::writeComment( std::string const& text ) { - ensureTagClosed(); - m_os << m_indent << ""; - m_needsNewline = true; - return *this; - } + auto encBytes = trailingBytes(c); + // Are there enough bytes left to avoid accessing out-of-bounds memory? + if (idx + encBytes - 1 >= m_str.size()) { + hexEscapeChar(os, c); + break; + } + // The header is valid, check data + // The next encBytes bytes must together be a valid utf-8 + // This means: bitpattern 10XX XXXX and the extracted value is sane (ish) + bool valid = true; + uint32_t value = headerValue(c); + for (std::size_t n = 1; n < encBytes; ++n) { + unsigned char nc = m_str[idx + n]; + valid &= ((nc & 0xC0) == 0x80); + value = (value << 6) | (nc & 0x3F); + } - void XmlWriter::writeStylesheetRef( std::string const& url ) { - m_os << "\n"; - } + if ( + // Wrong bit pattern of following bytes + (!valid) || + // Overlong encodings + (value < 0x80) || (0x80 <= value && value < 0x800 && encBytes > 2) || (0x800 < value && value < 0x10000 && encBytes > 3) || + // Encoded value out of range + (value >= 0x110000)) { + hexEscapeChar(os, c); + break; + } - XmlWriter& XmlWriter::writeBlankLine() { - ensureTagClosed(); - m_os << '\n'; - return *this; + // If we got here, this is in fact a valid(ish) utf-8 sequence + for (std::size_t n = 0; n < encBytes; ++n) { + os << m_str[idx + n]; + } + idx += encBytes - 1; + break; + } } + } - void XmlWriter::ensureTagClosed() { - if( m_tagIsOpen ) { - m_os << ">" << std::endl; - m_tagIsOpen = false; - } - } + std::ostream &operator<<(std::ostream &os, XmlEncode const &xmlEncode) { + xmlEncode.encodeTo(os); + return os; + } - void XmlWriter::writeDeclaration() { - m_os << "\n"; - } + XmlWriter::ScopedElement::ScopedElement(XmlWriter *writer, XmlFormatting fmt) + : m_writer(writer) + , m_fmt(fmt) { + } - void XmlWriter::newlineIfNecessary() { - if( m_needsNewline ) { - m_os << std::endl; - m_needsNewline = false; - } + XmlWriter::ScopedElement::ScopedElement(ScopedElement &&other) noexcept + : m_writer(other.m_writer) + , m_fmt(other.m_fmt) { + other.m_writer = nullptr; + other.m_fmt = XmlFormatting::None; + } + XmlWriter::ScopedElement &XmlWriter::ScopedElement::operator=(ScopedElement &&other) noexcept { + if (m_writer) { + m_writer->endElement(); } -} + m_writer = other.m_writer; + other.m_writer = nullptr; + m_fmt = other.m_fmt; + other.m_fmt = XmlFormatting::None; + return *this; + } + + XmlWriter::ScopedElement::~ScopedElement() { + if (m_writer) { + m_writer->endElement(m_fmt); + } + } + + XmlWriter::ScopedElement &XmlWriter::ScopedElement::writeText(std::string const &text, XmlFormatting fmt) { + m_writer->writeText(text, fmt); + return *this; + } + + XmlWriter::XmlWriter(std::ostream &os) + : m_os(os) { + writeDeclaration(); + } + + XmlWriter::~XmlWriter() { + while (!m_tags.empty()) { + endElement(); + } + newlineIfNecessary(); + } + + XmlWriter &XmlWriter::startElement(std::string const &name, XmlFormatting fmt) { + ensureTagClosed(); + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + m_indent += " "; + } + m_os << '<' << name; + m_tags.push_back(name); + m_tagIsOpen = true; + applyFormatting(fmt); + return *this; + } + + XmlWriter::ScopedElement XmlWriter::scopedElement(std::string const &name, XmlFormatting fmt) { + ScopedElement scoped(this, fmt); + startElement(name, fmt); + return scoped; + } + + XmlWriter &XmlWriter::endElement(XmlFormatting fmt) { + m_indent = m_indent.substr(0, m_indent.size() - 2); + + if (m_tagIsOpen) { + m_os << "/>"; + m_tagIsOpen = false; + } else { + newlineIfNecessary(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + } + m_os << std::flush; + applyFormatting(fmt); + m_tags.pop_back(); + return *this; + } + + XmlWriter &XmlWriter::writeAttribute(std::string const &name, std::string const &attribute) { + if (!name.empty() && !attribute.empty()) + m_os << ' ' << name << "=\"" << XmlEncode(attribute, XmlEncode::ForAttributes) << '"'; + return *this; + } + + XmlWriter &XmlWriter::writeAttribute(std::string const &name, bool attribute) { + m_os << ' ' << name << "=\"" << (attribute ? "true" : "false") << '"'; + return *this; + } + + XmlWriter &XmlWriter::writeText(std::string const &text, XmlFormatting fmt) { + if (!text.empty()) { + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if (tagWasOpen && shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << XmlEncode(text); + applyFormatting(fmt); + } + return *this; + } + + XmlWriter &XmlWriter::writeComment(std::string const &text, XmlFormatting fmt) { + ensureTagClosed(); + if (shouldIndent(fmt)) { + m_os << m_indent; + } + m_os << ""; + applyFormatting(fmt); + return *this; + } + + void XmlWriter::writeStylesheetRef(std::string const &url) { + m_os << "\n"; + } + + XmlWriter &XmlWriter::writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void XmlWriter::ensureTagClosed() { + if (m_tagIsOpen) { + m_os << '>' << std::flush; + newlineIfNecessary(); + m_tagIsOpen = false; + } + } + + void XmlWriter::applyFormatting(XmlFormatting fmt) { + m_needsNewline = shouldNewline(fmt); + } + + void XmlWriter::writeDeclaration() { + m_os << "\n"; + } + + void XmlWriter::newlineIfNecessary() { + if (m_needsNewline) { + m_os << std::endl; + m_needsNewline = false; + } + } +} // namespace Catch // end catch_xmlwriter.cpp // start catch_reporter_bases.cpp -#include +#include #include #include -#include +#include #include namespace Catch { - void prepareExpandedExpression(AssertionResult& result) { - result.getExpandedExpression(); - } + void prepareExpandedExpression(AssertionResult &result) { + result.getExpandedExpression(); + } - // Because formatting using c++ streams is stateful, drop down to C is required - // Alternatively we could use stringstream, but its performance is... not good. - std::string getFormattedDuration( double duration ) { - // Max exponent + 1 is required to represent the whole part - // + 1 for decimal point - // + 3 for the 3 decimal places - // + 1 for null terminator - const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; - char buffer[maxDoubleSize]; + // Because formatting using c++ streams is stateful, drop down to C is required + // Alternatively we could use stringstream, but its performance is... not good. + std::string getFormattedDuration(double duration) { + // Max exponent + 1 is required to represent the whole part + // + 1 for decimal point + // + 3 for the 3 decimal places + // + 1 for null terminator + const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1; + char buffer[maxDoubleSize]; - // Save previous errno, to prevent sprintf from overwriting it - ErrnoGuard guard; + // Save previous errno, to prevent sprintf from overwriting it + ErrnoGuard guard; #ifdef _MSC_VER - sprintf_s(buffer, "%.3f", duration); + sprintf_s(buffer, "%.3f", duration); #else - sprintf(buffer, "%.3f", duration); + std::sprintf(buffer, "%.3f", duration); #endif - return std::string(buffer); + return std::string(buffer); + } + + bool shouldShowDuration(IConfig const &config, double duration) { + if (config.showDurations() == ShowDurations::Always) { + return true; } - - TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config) - :StreamingReporterBase(_config) {} - - void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} - - bool TestEventListenerBase::assertionEnded(AssertionStats const &) { - return false; + if (config.showDurations() == ShowDurations::Never) { + return false; } + const double min = config.minDuration(); + return min >= 0 && duration >= min; + } + + std::string serializeFilters(std::vector const &container) { + ReusableStringStream oss; + bool first = true; + for (auto &&filter : container) { + if (!first) + oss << ' '; + else + first = false; + + oss << filter; + } + return oss.str(); + } + + TestEventListenerBase::TestEventListenerBase(ReporterConfig const &_config) + : StreamingReporterBase(_config) { + } + + std::set TestEventListenerBase::getSupportedVerbosities() { + return {Verbosity::Quiet, Verbosity::Normal, Verbosity::High}; + } + + void TestEventListenerBase::assertionStarting(AssertionInfo const &) {} + + bool TestEventListenerBase::assertionEnded(AssertionStats const &) { + return false; + } } // end namespace Catch // end catch_reporter_bases.cpp // start catch_reporter_compact.cpp namespace { - #ifdef CATCH_PLATFORM_MAC - const char* failedString() { return "FAILED"; } - const char* passedString() { return "PASSED"; } + const char *failedString() { + return "FAILED"; + } + const char *passedString() { + return "PASSED"; + } #else - const char* failedString() { return "failed"; } - const char* passedString() { return "passed"; } + const char *failedString() { + return "failed"; + } + const char *passedString() { + return "passed"; + } #endif - // Colour::LightGrey - Catch::Colour::Code dimColour() { return Catch::Colour::FileName; } + // Colour::LightGrey + Catch::Colour::Code dimColour() { + return Catch::Colour::FileName; + } - std::string bothOrAll( std::size_t count ) { - return count == 1 ? std::string() : - count == 2 ? "both " : "all " ; - } + std::string bothOrAll(std::size_t count) { + return count == 1 ? std::string() : count == 2 ? "both " + : "all "; + } -} // anon namespace +} // namespace namespace Catch { -namespace { -// Colour, message variants: -// - white: No tests ran. -// - red: Failed [both/all] N test cases, failed [both/all] M assertions. -// - white: Passed [both/all] N test cases (no assertions). -// - red: Failed N tests cases, failed M assertions. -// - green: Passed [both/all] N tests cases with M assertions. -void printTotals(std::ostream& out, const Totals& totals) { - if (totals.testCases.total() == 0) { + namespace { + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + void printTotals(std::ostream &out, const Totals &totals) { + if (totals.testCases.total() == 0) { out << "No tests ran."; - } else if (totals.testCases.failed == totals.testCases.total()) { + } else if (totals.testCases.failed == totals.testCases.total()) { Colour colour(Colour::ResultError); - const std::string qualify_assertions_failed = - totals.assertions.failed == totals.assertions.total() ? - bothOrAll(totals.assertions.failed) : std::string(); - out << - "Failed " << bothOrAll(totals.testCases.failed) - << pluralise(totals.testCases.failed, "test case") << ", " - "failed " << qualify_assertions_failed << - pluralise(totals.assertions.failed, "assertion") << '.'; - } else if (totals.assertions.total() == 0) { - out << - "Passed " << bothOrAll(totals.testCases.total()) - << pluralise(totals.testCases.total(), "test case") - << " (no assertions)."; - } else if (totals.assertions.failed) { + const std::string qualify_assertions_failed + = totals.assertions.failed == totals.assertions.total() ? bothOrAll(totals.assertions.failed) : std::string(); + out << "Failed " << bothOrAll(totals.testCases.failed) << pluralise(totals.testCases.failed, "test case") + << ", " + "failed " + << qualify_assertions_failed << pluralise(totals.assertions.failed, "assertion") << '.'; + } else if (totals.assertions.total() == 0) { + out << "Passed " << bothOrAll(totals.testCases.total()) << pluralise(totals.testCases.total(), "test case") << " (no assertions)."; + } else if (totals.assertions.failed) { Colour colour(Colour::ResultError); - out << - "Failed " << pluralise(totals.testCases.failed, "test case") << ", " - "failed " << pluralise(totals.assertions.failed, "assertion") << '.'; - } else { + out << "Failed " << pluralise(totals.testCases.failed, "test case") + << ", " + "failed " + << pluralise(totals.assertions.failed, "assertion") << '.'; + } else { Colour colour(Colour::ResultSuccess); - out << - "Passed " << bothOrAll(totals.testCases.passed) - << pluralise(totals.testCases.passed, "test case") << - " with " << pluralise(totals.assertions.passed, "assertion") << '.'; + out << "Passed " << bothOrAll(totals.testCases.passed) << pluralise(totals.testCases.passed, "test case") << " with " + << pluralise(totals.assertions.passed, "assertion") << '.'; + } } -} -// Implementation of CompactReporter formatting -class AssertionPrinter { -public: - AssertionPrinter& operator= (AssertionPrinter const&) = delete; - AssertionPrinter(AssertionPrinter const&) = delete; - AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) - : stream(_stream) - , result(_stats.assertionResult) - , messages(_stats.infoMessages) - , itMessage(_stats.infoMessages.begin()) - , printInfoMessages(_printInfoMessages) {} + // Implementation of CompactReporter formatting + class AssertionPrinter { + public: + AssertionPrinter &operator=(AssertionPrinter const &) = delete; + AssertionPrinter(AssertionPrinter const &) = delete; + AssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) + : stream(_stream) + , result(_stats.assertionResult) + , messages(_stats.infoMessages) + , itMessage(_stats.infoMessages.begin()) + , printInfoMessages(_printInfoMessages) { + } - void print() { + void print() { printSourceInfo(); itMessage = messages.begin(); switch (result.getResultType()) { - case ResultWas::Ok: + case ResultWas::Ok: printResultType(Colour::ResultSuccess, passedString()); printOriginalExpression(); printReconstructedExpression(); if (!result.hasExpression()) - printRemainingMessages(Colour::None); + printRemainingMessages(Colour::None); else - printRemainingMessages(); + printRemainingMessages(); break; - case ResultWas::ExpressionFailed: + case ResultWas::ExpressionFailed: if (result.isOk()) - printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); + printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok")); else - printResultType(Colour::Error, failedString()); + printResultType(Colour::Error, failedString()); printOriginalExpression(); printReconstructedExpression(); printRemainingMessages(); break; - case ResultWas::ThrewException: + case ResultWas::ThrewException: printResultType(Colour::Error, failedString()); printIssue("unexpected exception with message:"); printMessage(); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::FatalErrorCondition: + case ResultWas::FatalErrorCondition: printResultType(Colour::Error, failedString()); printIssue("fatal error condition with message:"); printMessage(); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::DidntThrowException: + case ResultWas::DidntThrowException: printResultType(Colour::Error, failedString()); printIssue("expected exception, got none"); printExpressionWas(); printRemainingMessages(); break; - case ResultWas::Info: + case ResultWas::Info: printResultType(Colour::None, "info"); printMessage(); printRemainingMessages(); break; - case ResultWas::Warning: + case ResultWas::Warning: printResultType(Colour::None, "warning"); printMessage(); printRemainingMessages(); break; - case ResultWas::ExplicitFailure: + case ResultWas::ExplicitFailure: printResultType(Colour::Error, failedString()); printIssue("explicitly"); printRemainingMessages(Colour::None); break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: printResultType(Colour::Error, "** internal error **"); break; } - } + } -private: - void printSourceInfo() const { + private: + void printSourceInfo() const { Colour colourGuard(Colour::FileName); stream << result.getSourceInfo() << ':'; - } + } - void printResultType(Colour::Code colour, std::string const& passOrFail) const { + void printResultType(Colour::Code colour, std::string const &passOrFail) const { if (!passOrFail.empty()) { - { - Colour colourGuard(colour); - stream << ' ' << passOrFail; - } - stream << ':'; + { + Colour colourGuard(colour); + stream << ' ' << passOrFail; + } + stream << ':'; } - } + } - void printIssue(std::string const& issue) const { - stream << ' ' << issue; - } + void printIssue(std::string const &issue) const { stream << ' ' << issue; } - void printExpressionWas() { + void printExpressionWas() { if (result.hasExpression()) { - stream << ';'; - { - Colour colour(dimColour()); - stream << " expression was:"; - } - printOriginalExpression(); + stream << ';'; + { + Colour colour(dimColour()); + stream << " expression was:"; + } + printOriginalExpression(); } - } + } - void printOriginalExpression() const { + void printOriginalExpression() const { if (result.hasExpression()) { - stream << ' ' << result.getExpression(); + stream << ' ' << result.getExpression(); } - } + } - void printReconstructedExpression() const { + void printReconstructedExpression() const { if (result.hasExpandedExpression()) { - { - Colour colour(dimColour()); - stream << " for: "; - } - stream << result.getExpandedExpression(); + { + Colour colour(dimColour()); + stream << " for: "; + } + stream << result.getExpandedExpression(); } - } + } - void printMessage() { + void printMessage() { if (itMessage != messages.end()) { - stream << " '" << itMessage->message << '\''; - ++itMessage; + stream << " '" << itMessage->message << '\''; + ++itMessage; } - } + } - void printRemainingMessages(Colour::Code colour = dimColour()) { + void printRemainingMessages(Colour::Code colour = dimColour()) { if (itMessage == messages.end()) - return; + return; - // using messages.end() directly yields (or auto) compilation error: - std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + const auto itEnd = messages.cend(); + const auto N = static_cast(std::distance(itMessage, itEnd)); { - Colour colourGuard(colour); - stream << " with " << pluralise(N, "message") << ':'; + Colour colourGuard(colour); + stream << " with " << pluralise(N, "message") << ':'; } - for (; itMessage != itEnd; ) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || itMessage->type != ResultWas::Info) { - stream << " '" << itMessage->message << '\''; - if (++itMessage != itEnd) { - Colour colourGuard(dimColour()); - stream << " and"; - } + while (itMessage != itEnd) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || itMessage->type != ResultWas::Info) { + printMessage(); + if (itMessage != itEnd) { + Colour colourGuard(dimColour()); + stream << " and"; } + continue; + } + ++itMessage; } + } + + private: + std::ostream &stream; + AssertionResult const &result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + } // namespace + + std::string CompactReporter::getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + void CompactReporter::noMatchingTestCases(std::string const &spec) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + void CompactReporter::assertionStarting(AssertionInfo const &) {} + + bool CompactReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if (!m_config->includeSuccessfulResults() && result.isOk()) { + if (result.getResultType() != ResultWas::Warning) + return false; + printInfoMessages = false; } -private: - std::ostream& stream; - AssertionResult const& result; - std::vector messages; - std::vector::const_iterator itMessage; - bool printInfoMessages; -}; + AssertionPrinter printer(stream, _assertionStats, printInfoMessages); + printer.print(); -} // anon namespace + stream << std::endl; + return true; + } - std::string CompactReporter::getDescription() { - return "Reports test results on a single line, suitable for IDEs"; - } + void CompactReporter::sectionEnded(SectionStats const &_sectionStats) { + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; + } + } - ReporterPreferences CompactReporter::getPreferences() const { - ReporterPreferences prefs; - prefs.shouldRedirectStdOut = false; - return prefs; - } + void CompactReporter::testRunEnded(TestRunStats const &_testRunStats) { + printTotals(stream, _testRunStats.totals); + stream << '\n' + << std::endl; + StreamingReporterBase::testRunEnded(_testRunStats); + } - void CompactReporter::noMatchingTestCases( std::string const& spec ) { - stream << "No test cases matched '" << spec << '\'' << std::endl; - } + CompactReporter::~CompactReporter() {} - void CompactReporter::assertionStarting( AssertionInfo const& ) {} - - bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) { - AssertionResult const& result = _assertionStats.assertionResult; - - bool printInfoMessages = true; - - // Drop out if result was successful and we're not printing those - if( !m_config->includeSuccessfulResults() && result.isOk() ) { - if( result.getResultType() != ResultWas::Warning ) - return false; - printInfoMessages = false; - } - - AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); - printer.print(); - - stream << std::endl; - return true; - } - - void CompactReporter::sectionEnded(SectionStats const& _sectionStats) { - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; - } - } - - void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) { - printTotals( stream, _testRunStats.totals ); - stream << '\n' << std::endl; - StreamingReporterBase::testRunEnded( _testRunStats ); - } - - CompactReporter::~CompactReporter() {} - - CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + CATCH_REGISTER_REPORTER("compact", CompactReporter) } // end namespace Catch // end catch_reporter_compact.cpp @@ -11570,352 +16215,370 @@ private: #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled - // and default is missing) is enabled +#pragma warning(disable : 4061) // Not all labels are EXPLICITLY handled in switch \ + // Note that 4062 (not all labels are handled and default is missing) is enabled +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +// For simplicity, benchmarking-only helpers are always enabled +#pragma clang diagnostic ignored "-Wunused-function" #endif namespace Catch { - -namespace { - -// Formatter impl for ConsoleReporter -class ConsoleAssertionPrinter { -public: - ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete; - ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete; - ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages) - : stream(_stream), - stats(_stats), - result(_stats.assertionResult), - colour(Colour::None), - message(result.getMessage()), - messages(_stats.infoMessages), - printInfoMessages(_printInfoMessages) { + namespace { + // Formatter impl for ConsoleReporter + class ConsoleAssertionPrinter { + public: + ConsoleAssertionPrinter &operator=(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(ConsoleAssertionPrinter const &) = delete; + ConsoleAssertionPrinter(std::ostream &_stream, AssertionStats const &_stats, bool _printInfoMessages) + : stream(_stream) + , stats(_stats) + , result(_stats.assertionResult) + , colour(Colour::None) + , message(result.getMessage()) + , messages(_stats.infoMessages) + , printInfoMessages(_printInfoMessages) { switch (result.getResultType()) { - case ResultWas::Ok: + case ResultWas::Ok: colour = Colour::Success; passOrFail = "PASSED"; - //if( result.hasMessage() ) + // if( result.hasMessage() ) if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; + messageLabel = "with message"; if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + messageLabel = "with messages"; break; - case ResultWas::ExpressionFailed: + case ResultWas::ExpressionFailed: if (result.isOk()) { - colour = Colour::Success; - passOrFail = "FAILED - but was ok"; + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; } else { - colour = Colour::Error; - passOrFail = "FAILED"; + colour = Colour::Error; + passOrFail = "FAILED"; } if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; + messageLabel = "with message"; if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + messageLabel = "with messages"; break; - case ResultWas::ThrewException: + case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to unexpected exception with "; if (_stats.infoMessages.size() == 1) - messageLabel += "message"; + messageLabel += "message"; if (_stats.infoMessages.size() > 1) - messageLabel += "messages"; + messageLabel += "messages"; break; - case ResultWas::FatalErrorCondition: + case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "due to a fatal error condition"; break; - case ResultWas::DidntThrowException: + case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"; messageLabel = "because no exception was thrown where one was expected"; break; - case ResultWas::Info: + case ResultWas::Info: messageLabel = "info"; break; - case ResultWas::Warning: + case ResultWas::Warning: messageLabel = "warning"; break; - case ResultWas::ExplicitFailure: + case ResultWas::ExplicitFailure: passOrFail = "FAILED"; colour = Colour::Error; if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; + messageLabel = "explicitly with message"; if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; + messageLabel = "explicitly with messages"; break; - // These cases are here to prevent compiler warnings - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: passOrFail = "** internal error **"; colour = Colour::Error; break; } - } + } - void print() const { + void print() const { printSourceInfo(); if (stats.totals.assertions.total() > 0) { - if (result.isOk()) - stream << '\n'; - printResultType(); - printOriginalExpression(); - printReconstructedExpression(); + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); } else { - stream << '\n'; + stream << '\n'; } printMessage(); - } + } -private: - void printResultType() const { + private: + void printResultType() const { if (!passOrFail.empty()) { - Colour colourGuard(colour); - stream << passOrFail << ":\n"; + Colour colourGuard(colour); + stream << passOrFail << ":\n"; } - } - void printOriginalExpression() const { + } + void printOriginalExpression() const { if (result.hasExpression()) { - Colour colourGuard(Colour::OriginalExpression); - stream << " "; - stream << result.getExpressionInMacro(); - stream << '\n'; + Colour colourGuard(Colour::OriginalExpression); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; } - } - void printReconstructedExpression() const { + } + void printReconstructedExpression() const { if (result.hasExpandedExpression()) { - stream << "with expansion:\n"; - Colour colourGuard(Colour::ReconstructedExpression); - stream << Column(result.getExpandedExpression()).indent(2) << '\n'; + stream << "with expansion:\n"; + Colour colourGuard(Colour::ReconstructedExpression); + stream << Column(result.getExpandedExpression()).indent(2) << '\n'; } - } - void printMessage() const { + } + void printMessage() const { if (!messageLabel.empty()) - stream << messageLabel << ':' << '\n'; - for (auto const& msg : messages) { - // If this assertion is a warning ignore any INFO messages - if (printInfoMessages || msg.type != ResultWas::Info) - stream << Column(msg.message).indent(2) << '\n'; + stream << messageLabel << ':' << '\n'; + for (auto const &msg : messages) { + // If this assertion is a warning ignore any INFO messages + if (printInfoMessages || msg.type != ResultWas::Info) + stream << Column(msg.message).indent(2) << '\n'; } - } - void printSourceInfo() const { + } + void printSourceInfo() const { Colour colourGuard(Colour::FileName); stream << result.getSourceInfo() << ": "; + } + + std::ostream &stream; + AssertionStats const &stats; + AssertionResult const &result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + std::size_t makeRatio(std::size_t number, std::size_t total) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; + return (ratio == 0 && number > 0) ? 1 : ratio; } - std::ostream& stream; - AssertionStats const& stats; - AssertionResult const& result; - Colour::Code colour; - std::string passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; - bool printInfoMessages; -}; - -std::size_t makeRatio(std::size_t number, std::size_t total) { - std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0; - return (ratio == 0 && number > 0) ? 1 : ratio; -} - -std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) { - if (i > j && i > k) + std::size_t &findMax(std::size_t &i, std::size_t &j, std::size_t &k) { + if (i > j && i > k) return i; - else if (j > k) + else if (j > k) return j; - else + else return k; -} + } -struct ColumnInfo { - enum Justification { Left, Right }; - std::string name; - int width; - Justification justification; -}; -struct ColumnBreak {}; -struct RowBreak {}; + struct ColumnInfo { + enum Justification { + Left, + Right + }; + std::string name; + int width; + Justification justification; + }; + struct ColumnBreak { + }; + struct RowBreak { + }; -class Duration { - enum class Unit { + class Duration { + enum class Unit { Auto, Nanoseconds, Microseconds, Milliseconds, Seconds, Minutes - }; - static const uint64_t s_nanosecondsInAMicrosecond = 1000; - static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; - static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; - static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; + }; + static const uint64_t s_nanosecondsInAMicrosecond = 1000; + static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond; + static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond; + static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond; - uint64_t m_inNanoseconds; - Unit m_units; + double m_inNanoseconds; + Unit m_units; -public: - explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto) - : m_inNanoseconds(inNanoseconds), - m_units(units) { + public: + explicit Duration(double inNanoseconds, Unit units = Unit::Auto) + : m_inNanoseconds(inNanoseconds) + , m_units(units) { if (m_units == Unit::Auto) { - if (m_inNanoseconds < s_nanosecondsInAMicrosecond) - m_units = Unit::Nanoseconds; - else if (m_inNanoseconds < s_nanosecondsInAMillisecond) - m_units = Unit::Microseconds; - else if (m_inNanoseconds < s_nanosecondsInASecond) - m_units = Unit::Milliseconds; - else if (m_inNanoseconds < s_nanosecondsInAMinute) - m_units = Unit::Seconds; - else - m_units = Unit::Minutes; + if (m_inNanoseconds < s_nanosecondsInAMicrosecond) + m_units = Unit::Nanoseconds; + else if (m_inNanoseconds < s_nanosecondsInAMillisecond) + m_units = Unit::Microseconds; + else if (m_inNanoseconds < s_nanosecondsInASecond) + m_units = Unit::Milliseconds; + else if (m_inNanoseconds < s_nanosecondsInAMinute) + m_units = Unit::Seconds; + else + m_units = Unit::Minutes; } + } - } - - auto value() const -> double { + auto value() const -> double { switch (m_units) { - case Unit::Microseconds: + case Unit::Microseconds: return m_inNanoseconds / static_cast(s_nanosecondsInAMicrosecond); - case Unit::Milliseconds: + case Unit::Milliseconds: return m_inNanoseconds / static_cast(s_nanosecondsInAMillisecond); - case Unit::Seconds: + case Unit::Seconds: return m_inNanoseconds / static_cast(s_nanosecondsInASecond); - case Unit::Minutes: + case Unit::Minutes: return m_inNanoseconds / static_cast(s_nanosecondsInAMinute); - default: - return static_cast(m_inNanoseconds); + default: + return m_inNanoseconds; } - } - auto unitsAsString() const -> std::string { + } + auto unitsAsString() const -> std::string { switch (m_units) { - case Unit::Nanoseconds: + case Unit::Nanoseconds: return "ns"; - case Unit::Microseconds: - return "µs"; - case Unit::Milliseconds: + case Unit::Microseconds: + return "us"; + case Unit::Milliseconds: return "ms"; - case Unit::Seconds: + case Unit::Seconds: return "s"; - case Unit::Minutes: + case Unit::Minutes: return "m"; - default: + default: return "** internal error **"; } + } + friend auto operator<<(std::ostream &os, Duration const &duration) -> std::ostream & { + return os << duration.value() << ' ' << duration.unitsAsString(); + } + }; + } // namespace - } - friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& { - return os << duration.value() << " " << duration.unitsAsString(); - } -}; -} // end anon namespace - -class TablePrinter { - std::ostream& m_os; + class TablePrinter { + std::ostream &m_os; std::vector m_columnInfos; std::ostringstream m_oss; int m_currentColumn = -1; bool m_isOpen = false; -public: - TablePrinter( std::ostream& os, std::vector columnInfos ) - : m_os( os ), - m_columnInfos( std::move( columnInfos ) ) {} - - auto columnInfos() const -> std::vector const& { - return m_columnInfos; + public: + TablePrinter(std::ostream &os, std::vector columnInfos) + : m_os(os) + , m_columnInfos(std::move(columnInfos)) { } + auto columnInfos() const -> std::vector const & { return m_columnInfos; } + void open() { - if (!m_isOpen) { - m_isOpen = true; - *this << RowBreak(); - for (auto const& info : m_columnInfos) - *this << info.name << ColumnBreak(); - *this << RowBreak(); - m_os << Catch::getLineOfChars<'-'>() << "\n"; + if (!m_isOpen) { + m_isOpen = true; + *this << RowBreak(); + + Columns headerCols; + Spacer spacer(2); + for (auto const &info : m_columnInfos) { + headerCols += Column(info.name).width(static_cast(info.width - 2)); + headerCols += spacer; } + m_os << headerCols << '\n'; + + m_os << Catch::getLineOfChars<'-'>() << '\n'; + } } void close() { - if (m_isOpen) { - *this << RowBreak(); - m_os << std::endl; - m_isOpen = false; - } + if (m_isOpen) { + *this << RowBreak(); + m_os << std::endl; + m_isOpen = false; + } } template - friend TablePrinter& operator << (TablePrinter& tp, T const& value) { - tp.m_oss << value; - return tp; + friend TablePrinter &operator<<(TablePrinter &tp, T const &value) { + tp.m_oss << value; + return tp; } - friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) { - auto colStr = tp.m_oss.str(); - // This takes account of utf8 encodings - auto strSize = Catch::StringRef(colStr).numberOfCharacters(); - tp.m_oss.str(""); - tp.open(); - if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { - tp.m_currentColumn = -1; - tp.m_os << "\n"; + friend TablePrinter &operator<<(TablePrinter &tp, ColumnBreak) { + auto colStr = tp.m_oss.str(); + const auto strSize = colStr.size(); + tp.m_oss.str(""); + tp.open(); + if (tp.m_currentColumn == static_cast(tp.m_columnInfos.size() - 1)) { + tp.m_currentColumn = -1; + tp.m_os << '\n'; + } + tp.m_currentColumn++; + + auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; + auto padding + = (strSize + 1 < static_cast(colInfo.width)) ? std::string(colInfo.width - (strSize + 1), ' ') : std::string(); + if (colInfo.justification == ColumnInfo::Left) + tp.m_os << colStr << padding << ' '; + else + tp.m_os << padding << colStr << ' '; + return tp; + } + + friend TablePrinter &operator<<(TablePrinter &tp, RowBreak) { + if (tp.m_currentColumn > 0) { + tp.m_os << '\n'; + tp.m_currentColumn = -1; + } + return tp; + } + }; + + ConsoleReporter::ConsoleReporter(ReporterConfig const &config) + : StreamingReporterBase(config) + , m_tablePrinter(new TablePrinter(config.stream(), [&config]() -> std::vector { + if (config.fullConfig()->benchmarkNoAnalysis()) { + return {{"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left}, + {" samples", 14, ColumnInfo::Right}, + {" iterations", 14, ColumnInfo::Right}, + {" mean", 14, ColumnInfo::Right}}; + } else { + return {{"benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left}, + {"samples mean std dev", 14, ColumnInfo::Right}, + {"iterations low mean low std dev", 14, ColumnInfo::Right}, + {"estimated high mean high std dev", 14, ColumnInfo::Right}}; } - tp.m_currentColumn++; + }())) { + } + ConsoleReporter::~ConsoleReporter() = default; - auto colInfo = tp.m_columnInfos[tp.m_currentColumn]; - auto padding = (strSize + 2 < static_cast(colInfo.width)) - ? std::string(colInfo.width - (strSize + 2), ' ') - : std::string(); - if (colInfo.justification == ColumnInfo::Left) - tp.m_os << colStr << padding << " "; - else - tp.m_os << padding << colStr << " "; - return tp; - } - - friend TablePrinter& operator << (TablePrinter& tp, RowBreak) { - if (tp.m_currentColumn > 0) { - tp.m_os << "\n"; - tp.m_currentColumn = -1; - } - return tp; - } -}; - -ConsoleReporter::ConsoleReporter(ReporterConfig const& config) - : StreamingReporterBase(config), - m_tablePrinter(new TablePrinter(config.stream(), - { - { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left }, - { "iters", 8, ColumnInfo::Right }, - { "elapsed ns", 14, ColumnInfo::Right }, - { "average", 14, ColumnInfo::Right } - })) {} -ConsoleReporter::~ConsoleReporter() = default; - -std::string ConsoleReporter::getDescription() { + std::string ConsoleReporter::getDescription() { return "Reports test results as plain lines of text"; -} + } -void ConsoleReporter::noMatchingTestCases(std::string const& spec) { + void ConsoleReporter::noMatchingTestCases(std::string const &spec) { stream << "No test cases matched '" << spec << '\'' << std::endl; -} + } -void ConsoleReporter::assertionStarting(AssertionInfo const&) {} + void ConsoleReporter::reportInvalidArguments(std::string const &arg) { + stream << "Invalid Filter: " << arg << std::endl; + } -bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { - AssertionResult const& result = _assertionStats.assertionResult; + void ConsoleReporter::assertionStarting(AssertionInfo const &) {} + + bool ConsoleReporter::assertionEnded(AssertionStats const &_assertionStats) { + AssertionResult const &result = _assertionStats.assertionResult; bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); // Drop out if result was successful but we're not printing them. if (!includeResults && result.getResultType() != ResultWas::Warning) - return false; + return false; lazyPrint(); @@ -11923,798 +16586,943 @@ bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) { printer.print(); stream << std::endl; return true; -} + } -void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) { + void ConsoleReporter::sectionStarting(SectionInfo const &_sectionInfo) { + m_tablePrinter->close(); m_headerPrinted = false; StreamingReporterBase::sectionStarting(_sectionInfo); -} -void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) { + } + void ConsoleReporter::sectionEnded(SectionStats const &_sectionStats) { m_tablePrinter->close(); if (_sectionStats.missingAssertions) { - lazyPrint(); - Colour colour(Colour::ResultError); - if (m_sectionStack.size() > 1) - stream << "\nNo assertions in section"; - else - stream << "\nNo assertions in test case"; - stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + lazyPrint(); + Colour colour(Colour::ResultError); + if (m_sectionStack.size() > 1) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" + << std::endl; } - if (m_config->showDurations() == ShowDurations::Always) { - stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl; + double dur = _sectionStats.durationInSeconds; + if (shouldShowDuration(*m_config, dur)) { + stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl; } if (m_headerPrinted) { - m_headerPrinted = false; + m_headerPrinted = false; } StreamingReporterBase::sectionEnded(_sectionStats); -} + } -void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) { +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void ConsoleReporter::benchmarkPreparing(std::string const &name) { lazyPrintWithoutClosingBenchmarkTable(); - auto nameCol = Column( info.name ).width( static_cast( m_tablePrinter->columnInfos()[0].width - 2 ) ); + auto nameCol = Column(name).width(static_cast(m_tablePrinter->columnInfos()[0].width - 2)); bool firstLine = true; for (auto line : nameCol) { - if (!firstLine) - (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); - else - firstLine = false; + if (!firstLine) + (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak(); + else + firstLine = false; - (*m_tablePrinter) << line << ColumnBreak(); + (*m_tablePrinter) << line << ColumnBreak(); } -} -void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) { - Duration average(stats.elapsedTimeInNanoseconds / stats.iterations); - (*m_tablePrinter) - << stats.iterations << ColumnBreak() - << stats.elapsedTimeInNanoseconds << ColumnBreak() - << average << ColumnBreak(); -} + } -void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { + void ConsoleReporter::benchmarkStarting(BenchmarkInfo const &info) { + (*m_tablePrinter) << info.samples << ColumnBreak() << info.iterations << ColumnBreak(); + if (!m_config->benchmarkNoAnalysis()) + (*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak(); + } + void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const &stats) { + if (m_config->benchmarkNoAnalysis()) { + (*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak(); + } else { + (*m_tablePrinter) << ColumnBreak() << Duration(stats.mean.point.count()) << ColumnBreak() + << Duration(stats.mean.lower_bound.count()) << ColumnBreak() << Duration(stats.mean.upper_bound.count()) + << ColumnBreak() << ColumnBreak() << Duration(stats.standardDeviation.point.count()) << ColumnBreak() + << Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak() + << Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() + << ColumnBreak() << ColumnBreak(); + } + } + + void ConsoleReporter::benchmarkFailed(std::string const &error) { + Colour colour(Colour::Red); + (*m_tablePrinter) << "Benchmark failed (" << error << ')' << ColumnBreak() << RowBreak(); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + void ConsoleReporter::testCaseEnded(TestCaseStats const &_testCaseStats) { m_tablePrinter->close(); StreamingReporterBase::testCaseEnded(_testCaseStats); m_headerPrinted = false; -} -void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) { + } + void ConsoleReporter::testGroupEnded(TestGroupStats const &_testGroupStats) { if (currentGroupInfo.used) { - printSummaryDivider(); - stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; - printTotals(_testGroupStats.totals); - stream << '\n' << std::endl; + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals(_testGroupStats.totals); + stream << '\n' + << std::endl; } StreamingReporterBase::testGroupEnded(_testGroupStats); -} -void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) { + } + void ConsoleReporter::testRunEnded(TestRunStats const &_testRunStats) { printTotalsDivider(_testRunStats.totals); printTotals(_testRunStats.totals); stream << std::endl; StreamingReporterBase::testRunEnded(_testRunStats); -} - -void ConsoleReporter::lazyPrint() { + } + void ConsoleReporter::testRunStarting(TestRunInfo const &_testInfo) { + StreamingReporterBase::testRunStarting(_testInfo); + printTestFilters(); + } + void ConsoleReporter::lazyPrint() { m_tablePrinter->close(); lazyPrintWithoutClosingBenchmarkTable(); -} - -void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { + } + void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() { if (!currentTestRunInfo.used) - lazyPrintRunInfo(); + lazyPrintRunInfo(); if (!currentGroupInfo.used) - lazyPrintGroupInfo(); + lazyPrintGroupInfo(); if (!m_headerPrinted) { - printTestCaseAndSectionHeader(); - m_headerPrinted = true; + printTestCaseAndSectionHeader(); + m_headerPrinted = true; } -} -void ConsoleReporter::lazyPrintRunInfo() { - stream << '\n' << getLineOfChars<'~'>() << '\n'; + } + void ConsoleReporter::lazyPrintRunInfo() { + stream << '\n' + << getLineOfChars<'~'>() << '\n'; Colour colour(Colour::SecondaryText); - stream << currentTestRunInfo->name - << " is a Catch v" << libraryVersion() << " host application.\n" - << "Run with -? for options\n\n"; + stream << currentTestRunInfo->name << " is a Catch v" << libraryVersion() << " host application.\n" + << "Run with -? for options\n\n"; if (m_config->rngSeed() != 0) - stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; currentTestRunInfo.used = true; -} -void ConsoleReporter::lazyPrintGroupInfo() { + } + void ConsoleReporter::lazyPrintGroupInfo() { if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) { - printClosedHeader("Group: " + currentGroupInfo->name); - currentGroupInfo.used = true; + printClosedHeader("Group: " + currentGroupInfo->name); + currentGroupInfo.used = true; } -} -void ConsoleReporter::printTestCaseAndSectionHeader() { + } + void ConsoleReporter::printTestCaseAndSectionHeader() { assert(!m_sectionStack.empty()); printOpenHeader(currentTestCaseInfo->name); if (m_sectionStack.size() > 1) { - Colour colourGuard(Colour::Headers); + Colour colourGuard(Colour::Headers); - auto - it = m_sectionStack.begin() + 1, // Skip first section (test case) - itEnd = m_sectionStack.end(); - for (; it != itEnd; ++it) - printHeaderString(it->name, 2); + auto it = m_sectionStack.begin() + 1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for (; it != itEnd; ++it) + printHeaderString(it->name, 2); } SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; - if (!lineInfo.empty()) { - stream << getLineOfChars<'-'>() << '\n'; - Colour colourGuard(Colour::FileName); - stream << lineInfo << '\n'; - } - stream << getLineOfChars<'.'>() << '\n' << std::endl; -} + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard(Colour::FileName); + stream << lineInfo << '\n'; + stream << getLineOfChars<'.'>() << '\n' + << std::endl; + } -void ConsoleReporter::printClosedHeader(std::string const& _name) { + void ConsoleReporter::printClosedHeader(std::string const &_name) { printOpenHeader(_name); stream << getLineOfChars<'.'>() << '\n'; -} -void ConsoleReporter::printOpenHeader(std::string const& _name) { + } + void ConsoleReporter::printOpenHeader(std::string const &_name) { stream << getLineOfChars<'-'>() << '\n'; { - Colour colourGuard(Colour::Headers); - printHeaderString(_name); + Colour colourGuard(Colour::Headers); + printHeaderString(_name); } -} + } -// if string has a : in first line will set indent to follow it on -// subsequent lines -void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) { + // if string has a : in first line will set indent to follow it on + // subsequent lines + void ConsoleReporter::printHeaderString(std::string const &_string, std::size_t indent) { std::size_t i = _string.find(": "); if (i != std::string::npos) - i += 2; + i += 2; else - i = 0; + i = 0; stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n'; -} + } -struct SummaryColumn { - - SummaryColumn( std::string _label, Colour::Code _colour ) - : label( std::move( _label ) ), - colour( _colour ) {} - SummaryColumn addRow( std::size_t count ) { - ReusableStringStream rss; - rss << count; - std::string row = rss.str(); - for (auto& oldRow : rows) { - while (oldRow.size() < row.size()) - oldRow = ' ' + oldRow; - while (oldRow.size() > row.size()) - row = ' ' + row; - } - rows.push_back(row); - return *this; + struct SummaryColumn { + SummaryColumn(std::string _label, Colour::Code _colour) + : label(std::move(_label)) + , colour(_colour) { + } + SummaryColumn addRow(std::size_t count) { + ReusableStringStream rss; + rss << count; + std::string row = rss.str(); + for (auto &oldRow : rows) { + while (oldRow.size() < row.size()) + oldRow = ' ' + oldRow; + while (oldRow.size() > row.size()) + row = ' ' + row; + } + rows.push_back(row); + return *this; } std::string label; Colour::Code colour; std::vector rows; + }; -}; - -void ConsoleReporter::printTotals( Totals const& totals ) { + void ConsoleReporter::printTotals(Totals const &totals) { if (totals.testCases.total() == 0) { - stream << Colour(Colour::Warning) << "No tests ran\n"; + stream << Colour(Colour::Warning) << "No tests ran\n"; } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) { - stream << Colour(Colour::ResultSuccess) << "All tests passed"; - stream << " (" - << pluralise(totals.assertions.passed, "assertion") << " in " - << pluralise(totals.testCases.passed, "test case") << ')' - << '\n'; + stream << Colour(Colour::ResultSuccess) << "All tests passed"; + stream << " (" << pluralise(totals.assertions.passed, "assertion") << " in " << pluralise(totals.testCases.passed, "test case") + << ')' << '\n'; } else { + std::vector columns; + columns.push_back(SummaryColumn("", Colour::None).addRow(totals.testCases.total()).addRow(totals.assertions.total())); + columns.push_back(SummaryColumn("passed", Colour::Success).addRow(totals.testCases.passed).addRow(totals.assertions.passed)); + columns.push_back(SummaryColumn("failed", Colour::ResultError).addRow(totals.testCases.failed).addRow(totals.assertions.failed)); + columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) + .addRow(totals.testCases.failedButOk) + .addRow(totals.assertions.failedButOk)); - std::vector columns; - columns.push_back(SummaryColumn("", Colour::None) - .addRow(totals.testCases.total()) - .addRow(totals.assertions.total())); - columns.push_back(SummaryColumn("passed", Colour::Success) - .addRow(totals.testCases.passed) - .addRow(totals.assertions.passed)); - columns.push_back(SummaryColumn("failed", Colour::ResultError) - .addRow(totals.testCases.failed) - .addRow(totals.assertions.failed)); - columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure) - .addRow(totals.testCases.failedButOk) - .addRow(totals.assertions.failedButOk)); - - printSummaryRow("test cases", columns, 0); - printSummaryRow("assertions", columns, 1); + printSummaryRow("test cases", columns, 0); + printSummaryRow("assertions", columns, 1); } -} -void ConsoleReporter::printSummaryRow(std::string const& label, std::vector const& cols, std::size_t row) { + } + void ConsoleReporter::printSummaryRow(std::string const &label, std::vector const &cols, std::size_t row) { for (auto col : cols) { - std::string value = col.rows[row]; - if (col.label.empty()) { - stream << label << ": "; - if (value != "0") - stream << value; - else - stream << Colour(Colour::Warning) << "- none -"; - } else if (value != "0") { - stream << Colour(Colour::LightGrey) << " | "; - stream << Colour(col.colour) - << value << ' ' << col.label; - } - } - stream << '\n'; -} - -void ConsoleReporter::printTotalsDivider(Totals const& totals) { - if (totals.testCases.total() > 0) { - std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); - std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); - std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); - while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)++; - while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) - findMax(failedRatio, failedButOkRatio, passedRatio)--; - - stream << Colour(Colour::Error) << std::string(failedRatio, '='); - stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); - if (totals.testCases.allPassed()) - stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + std::string value = col.rows[row]; + if (col.label.empty()) { + stream << label << ": "; + if (value != "0") + stream << value; else - stream << Colour(Colour::Success) << std::string(passedRatio, '='); - } else { - stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + stream << Colour(Colour::Warning) << "- none -"; + } else if (value != "0") { + stream << Colour(Colour::LightGrey) << " | "; + stream << Colour(col.colour) << value << ' ' << col.label; + } } stream << '\n'; -} -void ConsoleReporter::printSummaryDivider() { - stream << getLineOfChars<'-'>() << '\n'; -} + } -CATCH_REGISTER_REPORTER("console", ConsoleReporter) + void ConsoleReporter::printTotalsDivider(Totals const &totals) { + if (totals.testCases.total() > 0) { + std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total()); + std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total()); + std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total()); + while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)++; + while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1) + findMax(failedRatio, failedButOkRatio, passedRatio)--; + + stream << Colour(Colour::Error) << std::string(failedRatio, '='); + stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '='); + if (totals.testCases.allPassed()) + stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '='); + else + stream << Colour(Colour::Success) << std::string(passedRatio, '='); + } else { + stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '='); + } + stream << '\n'; + } + void ConsoleReporter::printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; + } + + void ConsoleReporter::printTestFilters() { + if (m_config->testSpec().hasFilters()) { + Colour guard(Colour::BrightYellow); + stream << "Filters: " << serializeFilters(m_config->getTestsOrTags()) << '\n'; + } + } + + CATCH_REGISTER_REPORTER("console", ConsoleReporter) } // end namespace Catch #if defined(_MSC_VER) #pragma warning(pop) #endif + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif // end catch_reporter_console.cpp // start catch_reporter_junit.cpp -#include -#include -#include #include +#include +#include +#include namespace Catch { - - namespace { - std::string getCurrentTimestamp() { - // Beware, this is not reentrant because of backward compatibility issues - // Also, UTC only, again because of backward compatibility (%z is C++11) - time_t rawtime; - std::time(&rawtime); - auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + auto const timeStampSize = sizeof("2017-01-16T17:06:45Z"); #ifdef _MSC_VER - std::tm timeInfo = {}; - gmtime_s(&timeInfo, &rawtime); + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); #else - std::tm* timeInfo; - timeInfo = std::gmtime(&rawtime); + std::tm *timeInfo; + timeInfo = std::gmtime(&rawtime); #endif - char timeStamp[timeStampSize]; - const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + char timeStamp[timeStampSize]; + const char *const fmt = "%Y-%m-%dT%H:%M:%SZ"; #ifdef _MSC_VER - std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); #else - std::strftime(timeStamp, timeStampSize, fmt, timeInfo); + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); #endif - return std::string(timeStamp); + return std::string(timeStamp); + } + + std::string fileNameTag(const std::vector &tags) { + auto it = std::find_if(begin(tags), end(tags), [](std::string const &tag) { return tag.front() == '#'; }); + if (it != tags.end()) + return it->substr(1); + return std::string(); + } + } // anonymous namespace + + JunitReporter::JunitReporter(ReporterConfig const &_config) + : CumulativeReporterBase(_config) + , xml(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + JunitReporter::~JunitReporter() {} + + std::string JunitReporter::getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + void JunitReporter::noMatchingTestCases(std::string const & /*spec*/) {} + + void JunitReporter::testRunStarting(TestRunInfo const &runInfo) { + CumulativeReporterBase::testRunStarting(runInfo); + xml.startElement("testsuites"); + } + + void JunitReporter::testGroupStarting(GroupInfo const &groupInfo) { + suiteTimer.start(); + stdOutForSuite.clear(); + stdErrForSuite.clear(); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting(groupInfo); + } + + void JunitReporter::testCaseStarting(TestCaseInfo const &testCaseInfo) { + m_okToFail = testCaseInfo.okToFail(); + } + + bool JunitReporter::assertionEnded(AssertionStats const &assertionStats) { + if (assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded(assertionStats); + } + + void JunitReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + stdOutForSuite += testCaseStats.stdOut; + stdErrForSuite += testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded(testCaseStats); + } + + void JunitReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded(testGroupStats); + writeGroup(*m_testGroups.back(), suiteTime); + } + + void JunitReporter::testRunEndedCumulative() { + xml.endElement(); + } + + void JunitReporter::writeGroup(TestGroupNode const &groupNode, double suiteTime) { + XmlWriter::ScopedElement e = xml.scopedElement("testsuite"); + + TestGroupStats const &stats = groupNode.value; + xml.writeAttribute("name", stats.groupInfo.name); + xml.writeAttribute("errors", unexpectedExceptions); + xml.writeAttribute("failures", stats.totals.assertions.failed - unexpectedExceptions); + xml.writeAttribute("tests", stats.totals.assertions.total()); + xml.writeAttribute("hostname", "tbd"); // !TBD + if (m_config->showDurations() == ShowDurations::Never) + xml.writeAttribute("time", ""); + else + xml.writeAttribute("time", suiteTime); + xml.writeAttribute("timestamp", getCurrentTimestamp()); + + // Write properties if there are any + if (m_config->hasTestFilters() || m_config->rngSeed() != 0) { + auto properties = xml.scopedElement("properties"); + if (m_config->hasTestFilters()) { + xml.scopedElement("property").writeAttribute("name", "filters").writeAttribute("value", serializeFilters(m_config->getTestsOrTags())); + } + if (m_config->rngSeed() != 0) { + xml.scopedElement("property").writeAttribute("name", "random-seed").writeAttribute("value", m_config->rngSeed()); + } + } + + // Write test cases + for (auto const &child : groupNode.children) + writeTestCase(*child); + + xml.scopedElement("system-out").writeText(trim(stdOutForSuite), XmlFormatting::Newline); + xml.scopedElement("system-err").writeText(trim(stdErrForSuite), XmlFormatting::Newline); + } + + void JunitReporter::writeTestCase(TestCaseNode const &testCaseNode) { + TestCaseStats const &stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert(testCaseNode.children.size() == 1); + SectionNode const &rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if (className.empty()) { + className = fileNameTag(stats.testInfo.tags); + if (className.empty()) + className = "global"; + } + + if (!m_config->name().empty()) + className = m_config->name() + "." + className; + + writeSection(className, "", rootSection); + } + + void JunitReporter::writeSection(std::string const &className, std::string const &rootName, SectionNode const §ionNode) { + std::string name = trim(sectionNode.stats.sectionInfo.name); + if (!rootName.empty()) + name = rootName + '/' + name; + + if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) { + XmlWriter::ScopedElement e = xml.scopedElement("testcase"); + if (className.empty()) { + xml.writeAttribute("classname", name); + xml.writeAttribute("name", "root"); + } else { + xml.writeAttribute("classname", className); + xml.writeAttribute("name", name); + } + xml.writeAttribute("time", ::Catch::Detail::stringify(sectionNode.stats.durationInSeconds)); + // This is not ideal, but it should be enough to mimic gtest's + // junit output. + // Ideally the JUnit reporter would also handle `skipTest` + // events and write those out appropriately. + xml.writeAttribute("status", "run"); + + writeAssertions(sectionNode); + + if (!sectionNode.stdOut.empty()) + xml.scopedElement("system-out").writeText(trim(sectionNode.stdOut), XmlFormatting::Newline); + if (!sectionNode.stdErr.empty()) + xml.scopedElement("system-err").writeText(trim(sectionNode.stdErr), XmlFormatting::Newline); + } + for (auto const &childNode : sectionNode.childSections) + if (className.empty()) + writeSection(name, "", *childNode); + else + writeSection(className, name, *childNode); + } + + void JunitReporter::writeAssertions(SectionNode const §ionNode) { + for (auto const &assertion : sectionNode.assertions) + writeAssertion(assertion); + } + + void JunitReporter::writeAssertion(AssertionStats const &stats) { + AssertionResult const &result = stats.assertionResult; + if (!result.isOk()) { + std::string elementName; + switch (result.getResultType()) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + case ResultWas::ExpressionFailed: + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement(elementName); + + xml.writeAttribute("message", result.getExpression()); + xml.writeAttribute("type", result.getTestMacroName()); + + ReusableStringStream rss; + if (stats.totals.assertions.total() > 0) { + rss << "FAILED" + << ":\n"; + if (result.hasExpression()) { + rss << " "; + rss << result.getExpressionInMacro(); + rss << '\n'; } - - std::string fileNameTag(const std::vector &tags) { - auto it = std::find_if(begin(tags), - end(tags), - [] (std::string const& tag) {return tag.front() == '#'; }); - if (it != tags.end()) - return it->substr(1); - return std::string(); + if (result.hasExpandedExpression()) { + rss << "with expansion:\n"; + rss << Column(result.getExpandedExpression()).indent(2) << '\n'; } - } // anonymous namespace + } else { + rss << '\n'; + } - JunitReporter::JunitReporter( ReporterConfig const& _config ) - : CumulativeReporterBase( _config ), - xml( _config.stream() ) - { - m_reporterPrefs.shouldRedirectStdOut = true; - } + if (!result.getMessage().empty()) + rss << result.getMessage() << '\n'; + for (auto const &msg : stats.infoMessages) + if (msg.type == ResultWas::Info) + rss << msg.message << '\n'; - JunitReporter::~JunitReporter() {} - - std::string JunitReporter::getDescription() { - return "Reports test results in an XML format that looks like Ant's junitreport target"; + rss << "at " << result.getSourceInfo(); + xml.writeText(rss.str(), XmlFormatting::Newline); } + } - void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {} - - void JunitReporter::testRunStarting( TestRunInfo const& runInfo ) { - CumulativeReporterBase::testRunStarting( runInfo ); - xml.startElement( "testsuites" ); - } - - void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) { - suiteTimer.start(); - stdOutForSuite.clear(); - stdErrForSuite.clear(); - unexpectedExceptions = 0; - CumulativeReporterBase::testGroupStarting( groupInfo ); - } - - void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) { - m_okToFail = testCaseInfo.okToFail(); - } - - bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) { - if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail ) - unexpectedExceptions++; - return CumulativeReporterBase::assertionEnded( assertionStats ); - } - - void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { - stdOutForSuite += testCaseStats.stdOut; - stdErrForSuite += testCaseStats.stdErr; - CumulativeReporterBase::testCaseEnded( testCaseStats ); - } - - void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { - double suiteTime = suiteTimer.getElapsedSeconds(); - CumulativeReporterBase::testGroupEnded( testGroupStats ); - writeGroup( *m_testGroups.back(), suiteTime ); - } - - void JunitReporter::testRunEndedCumulative() { - xml.endElement(); - } - - void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); - TestGroupStats const& stats = groupNode.value; - xml.writeAttribute( "name", stats.groupInfo.name ); - xml.writeAttribute( "errors", unexpectedExceptions ); - xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); - xml.writeAttribute( "tests", stats.totals.assertions.total() ); - xml.writeAttribute( "hostname", "tbd" ); // !TBD - if( m_config->showDurations() == ShowDurations::Never ) - xml.writeAttribute( "time", "" ); - else - xml.writeAttribute( "time", suiteTime ); - xml.writeAttribute( "timestamp", getCurrentTimestamp() ); - - // Write test cases - for( auto const& child : groupNode.children ) - writeTestCase( *child ); - - xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false ); - xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false ); - } - - void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) { - TestCaseStats const& stats = testCaseNode.value; - - // All test cases have exactly one section - which represents the - // test case itself. That section may have 0-n nested sections - assert( testCaseNode.children.size() == 1 ); - SectionNode const& rootSection = *testCaseNode.children.front(); - - std::string className = stats.testInfo.className; - - if( className.empty() ) { - className = fileNameTag(stats.testInfo.tags); - if ( className.empty() ) - className = "global"; - } - - if ( !m_config->name().empty() ) - className = m_config->name() + "." + className; - - writeSection( className, "", rootSection ); - } - - void JunitReporter::writeSection( std::string const& className, - std::string const& rootName, - SectionNode const& sectionNode ) { - std::string name = trim( sectionNode.stats.sectionInfo.name ); - if( !rootName.empty() ) - name = rootName + '/' + name; - - if( !sectionNode.assertions.empty() || - !sectionNode.stdOut.empty() || - !sectionNode.stdErr.empty() ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - if( className.empty() ) { - xml.writeAttribute( "classname", name ); - xml.writeAttribute( "name", "root" ); - } - else { - xml.writeAttribute( "classname", className ); - xml.writeAttribute( "name", name ); - } - xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) ); - - writeAssertions( sectionNode ); - - if( !sectionNode.stdOut.empty() ) - xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); - if( !sectionNode.stdErr.empty() ) - xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); - } - for( auto const& childNode : sectionNode.childSections ) - if( className.empty() ) - writeSection( name, "", *childNode ); - else - writeSection( className, name, *childNode ); - } - - void JunitReporter::writeAssertions( SectionNode const& sectionNode ) { - for( auto const& assertion : sectionNode.assertions ) - writeAssertion( assertion ); - } - - void JunitReporter::writeAssertion( AssertionStats const& stats ) { - AssertionResult const& result = stats.assertionResult; - if( !result.isOk() ) { - std::string elementName; - switch( result.getResultType() ) { - case ResultWas::ThrewException: - case ResultWas::FatalErrorCondition: - elementName = "error"; - break; - case ResultWas::ExplicitFailure: - elementName = "failure"; - break; - case ResultWas::ExpressionFailed: - elementName = "failure"; - break; - case ResultWas::DidntThrowException: - elementName = "failure"; - break; - - // We should never see these here: - case ResultWas::Info: - case ResultWas::Warning: - case ResultWas::Ok: - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - elementName = "internalError"; - break; - } - - XmlWriter::ScopedElement e = xml.scopedElement( elementName ); - - xml.writeAttribute( "message", result.getExpandedExpression() ); - xml.writeAttribute( "type", result.getTestMacroName() ); - - ReusableStringStream rss; - if( !result.getMessage().empty() ) - rss << result.getMessage() << '\n'; - for( auto const& msg : stats.infoMessages ) - if( msg.type == ResultWas::Info ) - rss << msg.message << '\n'; - - rss << "at " << result.getSourceInfo(); - xml.writeText( rss.str(), false ); - } - } - - CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + CATCH_REGISTER_REPORTER("junit", JunitReporter) } // end namespace Catch // end catch_reporter_junit.cpp -// start catch_reporter_multi.cpp +// start catch_reporter_listening.cpp + +#include namespace Catch { + ListeningReporter::ListeningReporter() { + // We will assume that listeners will always want all assertions + m_preferences.shouldReportAllAssertions = true; + } - void MultipleReporters::add( IStreamingReporterPtr&& reporter ) { - m_reporters.push_back( std::move( reporter ) ); - } + void ListeningReporter::addListener(IStreamingReporterPtr &&listener) { + m_listeners.push_back(std::move(listener)); + } - ReporterPreferences MultipleReporters::getPreferences() const { - return m_reporters[0]->getPreferences(); - } + void ListeningReporter::addReporter(IStreamingReporterPtr &&reporter) { + assert(!m_reporter && "Listening reporter can wrap only 1 real reporter"); + m_reporter = std::move(reporter); + m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut; + } - std::set MultipleReporters::getSupportedVerbosities() { - return std::set{ }; - } + ReporterPreferences ListeningReporter::getPreferences() const { + return m_preferences; + } - void MultipleReporters::noMatchingTestCases( std::string const& spec ) { - for( auto const& reporter : m_reporters ) - reporter->noMatchingTestCases( spec ); - } + std::set ListeningReporter::getSupportedVerbosities() { + return std::set{}; + } - void MultipleReporters::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) { - for( auto const& reporter : m_reporters ) - reporter->benchmarkStarting( benchmarkInfo ); - } - void MultipleReporters::benchmarkEnded( BenchmarkStats const& benchmarkStats ) { - for( auto const& reporter : m_reporters ) - reporter->benchmarkEnded( benchmarkStats ); + void ListeningReporter::noMatchingTestCases(std::string const &spec) { + for (auto const &listener : m_listeners) { + listener->noMatchingTestCases(spec); } + m_reporter->noMatchingTestCases(spec); + } - void MultipleReporters::testRunStarting( TestRunInfo const& testRunInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testRunStarting( testRunInfo ); + void ListeningReporter::reportInvalidArguments(std::string const &arg) { + for (auto const &listener : m_listeners) { + listener->reportInvalidArguments(arg); } + m_reporter->reportInvalidArguments(arg); + } - void MultipleReporters::testGroupStarting( GroupInfo const& groupInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testGroupStarting( groupInfo ); +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void ListeningReporter::benchmarkPreparing(std::string const &name) { + for (auto const &listener : m_listeners) { + listener->benchmarkPreparing(name); } + m_reporter->benchmarkPreparing(name); + } + void ListeningReporter::benchmarkStarting(BenchmarkInfo const &benchmarkInfo) { + for (auto const &listener : m_listeners) { + listener->benchmarkStarting(benchmarkInfo); + } + m_reporter->benchmarkStarting(benchmarkInfo); + } + void ListeningReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) { + for (auto const &listener : m_listeners) { + listener->benchmarkEnded(benchmarkStats); + } + m_reporter->benchmarkEnded(benchmarkStats); + } - void MultipleReporters::testCaseStarting( TestCaseInfo const& testInfo ) { - for( auto const& reporter : m_reporters ) - reporter->testCaseStarting( testInfo ); + void ListeningReporter::benchmarkFailed(std::string const &error) { + for (auto const &listener : m_listeners) { + listener->benchmarkFailed(error); } + m_reporter->benchmarkFailed(error); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING - void MultipleReporters::sectionStarting( SectionInfo const& sectionInfo ) { - for( auto const& reporter : m_reporters ) - reporter->sectionStarting( sectionInfo ); + void ListeningReporter::testRunStarting(TestRunInfo const &testRunInfo) { + for (auto const &listener : m_listeners) { + listener->testRunStarting(testRunInfo); } + m_reporter->testRunStarting(testRunInfo); + } - void MultipleReporters::assertionStarting( AssertionInfo const& assertionInfo ) { - for( auto const& reporter : m_reporters ) - reporter->assertionStarting( assertionInfo ); + void ListeningReporter::testGroupStarting(GroupInfo const &groupInfo) { + for (auto const &listener : m_listeners) { + listener->testGroupStarting(groupInfo); } + m_reporter->testGroupStarting(groupInfo); + } - // The return value indicates if the messages buffer should be cleared: - bool MultipleReporters::assertionEnded( AssertionStats const& assertionStats ) { - bool clearBuffer = false; - for( auto const& reporter : m_reporters ) - clearBuffer |= reporter->assertionEnded( assertionStats ); - return clearBuffer; + void ListeningReporter::testCaseStarting(TestCaseInfo const &testInfo) { + for (auto const &listener : m_listeners) { + listener->testCaseStarting(testInfo); } + m_reporter->testCaseStarting(testInfo); + } - void MultipleReporters::sectionEnded( SectionStats const& sectionStats ) { - for( auto const& reporter : m_reporters ) - reporter->sectionEnded( sectionStats ); + void ListeningReporter::sectionStarting(SectionInfo const §ionInfo) { + for (auto const &listener : m_listeners) { + listener->sectionStarting(sectionInfo); } + m_reporter->sectionStarting(sectionInfo); + } - void MultipleReporters::testCaseEnded( TestCaseStats const& testCaseStats ) { - for( auto const& reporter : m_reporters ) - reporter->testCaseEnded( testCaseStats ); + void ListeningReporter::assertionStarting(AssertionInfo const &assertionInfo) { + for (auto const &listener : m_listeners) { + listener->assertionStarting(assertionInfo); } + m_reporter->assertionStarting(assertionInfo); + } - void MultipleReporters::testGroupEnded( TestGroupStats const& testGroupStats ) { - for( auto const& reporter : m_reporters ) - reporter->testGroupEnded( testGroupStats ); + // The return value indicates if the messages buffer should be cleared: + bool ListeningReporter::assertionEnded(AssertionStats const &assertionStats) { + for (auto const &listener : m_listeners) { + static_cast(listener->assertionEnded(assertionStats)); } + return m_reporter->assertionEnded(assertionStats); + } - void MultipleReporters::testRunEnded( TestRunStats const& testRunStats ) { - for( auto const& reporter : m_reporters ) - reporter->testRunEnded( testRunStats ); + void ListeningReporter::sectionEnded(SectionStats const §ionStats) { + for (auto const &listener : m_listeners) { + listener->sectionEnded(sectionStats); } + m_reporter->sectionEnded(sectionStats); + } - void MultipleReporters::skipTest( TestCaseInfo const& testInfo ) { - for( auto const& reporter : m_reporters ) - reporter->skipTest( testInfo ); + void ListeningReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + for (auto const &listener : m_listeners) { + listener->testCaseEnded(testCaseStats); } + m_reporter->testCaseEnded(testCaseStats); + } - bool MultipleReporters::isMulti() const { - return true; + void ListeningReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + for (auto const &listener : m_listeners) { + listener->testGroupEnded(testGroupStats); } + m_reporter->testGroupEnded(testGroupStats); + } + + void ListeningReporter::testRunEnded(TestRunStats const &testRunStats) { + for (auto const &listener : m_listeners) { + listener->testRunEnded(testRunStats); + } + m_reporter->testRunEnded(testRunStats); + } + + void ListeningReporter::skipTest(TestCaseInfo const &testInfo) { + for (auto const &listener : m_listeners) { + listener->skipTest(testInfo); + } + m_reporter->skipTest(testInfo); + } + + bool ListeningReporter::isMulti() const { + return true; + } } // end namespace Catch -// end catch_reporter_multi.cpp +// end catch_reporter_listening.cpp // start catch_reporter_xml.cpp #if defined(_MSC_VER) #pragma warning(push) -#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch - // Note that 4062 (not all labels are handled - // and default is missing) is enabled +#pragma warning(disable : 4061) // Not all labels are EXPLICITLY handled in switch \ + // Note that 4062 (not all labels are handled \ + // and default is missing) is enabled #endif namespace Catch { - XmlReporter::XmlReporter( ReporterConfig const& _config ) - : StreamingReporterBase( _config ), - m_xml(_config.stream()) - { - m_reporterPrefs.shouldRedirectStdOut = true; + XmlReporter::XmlReporter(ReporterConfig const &_config) + : StreamingReporterBase(_config) + , m_xml(_config.stream()) { + m_reporterPrefs.shouldRedirectStdOut = true; + m_reporterPrefs.shouldReportAllAssertions = true; + } + + XmlReporter::~XmlReporter() = default; + + std::string XmlReporter::getDescription() { + return "Reports test results as an XML document"; + } + + std::string XmlReporter::getStylesheetRef() const { + return std::string(); + } + + void XmlReporter::writeSourceInfo(SourceLineInfo const &sourceInfo) { + m_xml.writeAttribute("filename", sourceInfo.file).writeAttribute("line", sourceInfo.line); + } + + void XmlReporter::noMatchingTestCases(std::string const &s) { + StreamingReporterBase::noMatchingTestCases(s); + } + + void XmlReporter::testRunStarting(TestRunInfo const &testInfo) { + StreamingReporterBase::testRunStarting(testInfo); + std::string stylesheetRef = getStylesheetRef(); + if (!stylesheetRef.empty()) + m_xml.writeStylesheetRef(stylesheetRef); + m_xml.startElement("Catch"); + if (!m_config->name().empty()) + m_xml.writeAttribute("name", m_config->name()); + if (m_config->testSpec().hasFilters()) + m_xml.writeAttribute("filters", serializeFilters(m_config->getTestsOrTags())); + if (m_config->rngSeed() != 0) + m_xml.scopedElement("Randomness").writeAttribute("seed", m_config->rngSeed()); + } + + void XmlReporter::testGroupStarting(GroupInfo const &groupInfo) { + StreamingReporterBase::testGroupStarting(groupInfo); + m_xml.startElement("Group").writeAttribute("name", groupInfo.name); + } + + void XmlReporter::testCaseStarting(TestCaseInfo const &testInfo) { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement("TestCase") + .writeAttribute("name", trim(testInfo.name)) + .writeAttribute("description", testInfo.description) + .writeAttribute("tags", testInfo.tagsAsString()); + + writeSourceInfo(testInfo.lineInfo); + + if (m_config->showDurations() == ShowDurations::Always) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + void XmlReporter::sectionStarting(SectionInfo const §ionInfo) { + StreamingReporterBase::sectionStarting(sectionInfo); + if (m_sectionDepth++ > 0) { + m_xml.startElement("Section").writeAttribute("name", trim(sectionInfo.name)); + writeSourceInfo(sectionInfo.lineInfo); + m_xml.ensureTagClosed(); } + } - XmlReporter::~XmlReporter() = default; + void XmlReporter::assertionStarting(AssertionInfo const &) {} - std::string XmlReporter::getDescription() { - return "Reports test results as an XML document"; - } + bool XmlReporter::assertionEnded(AssertionStats const &assertionStats) { + AssertionResult const &result = assertionStats.assertionResult; - std::string XmlReporter::getStylesheetRef() const { - return std::string(); - } + bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) { - m_xml - .writeAttribute( "filename", sourceInfo.file ) - .writeAttribute( "line", sourceInfo.line ); - } - - void XmlReporter::noMatchingTestCases( std::string const& s ) { - StreamingReporterBase::noMatchingTestCases( s ); - } - - void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) { - StreamingReporterBase::testRunStarting( testInfo ); - std::string stylesheetRef = getStylesheetRef(); - if( !stylesheetRef.empty() ) - m_xml.writeStylesheetRef( stylesheetRef ); - m_xml.startElement( "Catch" ); - if( !m_config->name().empty() ) - m_xml.writeAttribute( "name", m_config->name() ); - } - - void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) { - StreamingReporterBase::testGroupStarting( groupInfo ); - m_xml.startElement( "Group" ) - .writeAttribute( "name", groupInfo.name ); - } - - void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { - StreamingReporterBase::testCaseStarting(testInfo); - m_xml.startElement( "TestCase" ) - .writeAttribute( "name", trim( testInfo.name ) ) - .writeAttribute( "description", testInfo.description ) - .writeAttribute( "tags", testInfo.tagsAsString() ); - - writeSourceInfo( testInfo.lineInfo ); - - if ( m_config->showDurations() == ShowDurations::Always ) - m_testCaseTimer.start(); - m_xml.ensureTagClosed(); - } - - void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) { - StreamingReporterBase::sectionStarting( sectionInfo ); - if( m_sectionDepth++ > 0 ) { - m_xml.startElement( "Section" ) - .writeAttribute( "name", trim( sectionInfo.name ) ) - .writeAttribute( "description", sectionInfo.description ); - writeSourceInfo( sectionInfo.lineInfo ); - m_xml.ensureTagClosed(); + if (includeResults || result.getResultType() == ResultWas::Warning) { + // Print any info messages in tags. + for (auto const &msg : assertionStats.infoMessages) { + if (msg.type == ResultWas::Info && includeResults) { + m_xml.scopedElement("Info").writeText(msg.message); + } else if (msg.type == ResultWas::Warning) { + m_xml.scopedElement("Warning").writeText(msg.message); } + } } - void XmlReporter::assertionStarting( AssertionInfo const& ) { } + // Drop out if result was successful but we're not printing them. + if (!includeResults && result.getResultType() != ResultWas::Warning) + return true; - bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) { + // Print the expression if there is one. + if (result.hasExpression()) { + m_xml.startElement("Expression").writeAttribute("success", result.succeeded()).writeAttribute("type", result.getTestMacroName()); - AssertionResult const& result = assertionStats.assertionResult; + writeSourceInfo(result.getSourceInfo()); - bool includeResults = m_config->includeSuccessfulResults() || !result.isOk(); - - if( includeResults || result.getResultType() == ResultWas::Warning ) { - // Print any info messages in tags. - for( auto const& msg : assertionStats.infoMessages ) { - if( msg.type == ResultWas::Info && includeResults ) { - m_xml.scopedElement( "Info" ) - .writeText( msg.message ); - } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); - } - } - } - - // Drop out if result was successful but we're not printing them. - if( !includeResults && result.getResultType() != ResultWas::Warning ) - return true; - - // Print the expression if there is one. - if( result.hasExpression() ) { - m_xml.startElement( "Expression" ) - .writeAttribute( "success", result.succeeded() ) - .writeAttribute( "type", result.getTestMacroName() ); - - writeSourceInfo( result.getSourceInfo() ); - - m_xml.scopedElement( "Original" ) - .writeText( result.getExpression() ); - m_xml.scopedElement( "Expanded" ) - .writeText( result.getExpandedExpression() ); - } - - // And... Print a result applicable to each result type. - switch( result.getResultType() ) { - case ResultWas::ThrewException: - m_xml.startElement( "Exception" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::FatalErrorCondition: - m_xml.startElement( "FatalErrorCondition" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - case ResultWas::Info: - m_xml.scopedElement( "Info" ) - .writeText( result.getMessage() ); - break; - case ResultWas::Warning: - // Warning will already have been written - break; - case ResultWas::ExplicitFailure: - m_xml.startElement( "Failure" ); - writeSourceInfo( result.getSourceInfo() ); - m_xml.writeText( result.getMessage() ); - m_xml.endElement(); - break; - default: - break; - } - - if( result.hasExpression() ) - m_xml.endElement(); - - return true; + m_xml.scopedElement("Original").writeText(result.getExpression()); + m_xml.scopedElement("Expanded").writeText(result.getExpandedExpression()); } - void XmlReporter::sectionEnded( SectionStats const& sectionStats ) { - StreamingReporterBase::sectionEnded( sectionStats ); - if( --m_sectionDepth > 0 ) { - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); - e.writeAttribute( "successes", sectionStats.assertions.passed ); - e.writeAttribute( "failures", sectionStats.assertions.failed ); - e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); - - m_xml.endElement(); - } - } - - void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) { - StreamingReporterBase::testCaseEnded( testCaseStats ); - XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); - e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); - - if ( m_config->showDurations() == ShowDurations::Always ) - e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); - - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); - if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); - + // And... Print a result applicable to each result type. + switch (result.getResultType()) { + case ResultWas::ThrewException: + m_xml.startElement("Exception"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); - } - - void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) { - StreamingReporterBase::testGroupEnded( testGroupStats ); - // TODO: Check testGroupStats.aborting and act accordingly. - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) - .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + break; + case ResultWas::FatalErrorCondition: + m_xml.startElement("FatalErrorCondition"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); - } - - void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) { - StreamingReporterBase::testRunEnded( testRunStats ); - m_xml.scopedElement( "OverallResults" ) - .writeAttribute( "successes", testRunStats.totals.assertions.passed ) - .writeAttribute( "failures", testRunStats.totals.assertions.failed ) - .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + break; + case ResultWas::Info: + m_xml.scopedElement("Info").writeText(result.getMessage()); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.startElement("Failure"); + writeSourceInfo(result.getSourceInfo()); + m_xml.writeText(result.getMessage()); m_xml.endElement(); + break; + default: + break; } - CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + if (result.hasExpression()) + m_xml.endElement(); + + return true; + } + + void XmlReporter::sectionEnded(SectionStats const §ionStats) { + StreamingReporterBase::sectionEnded(sectionStats); + if (--m_sectionDepth > 0) { + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResults"); + e.writeAttribute("successes", sectionStats.assertions.passed); + e.writeAttribute("failures", sectionStats.assertions.failed); + e.writeAttribute("expectedFailures", sectionStats.assertions.failedButOk); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", sectionStats.durationInSeconds); + + m_xml.endElement(); + } + } + + void XmlReporter::testCaseEnded(TestCaseStats const &testCaseStats) { + StreamingReporterBase::testCaseEnded(testCaseStats); + XmlWriter::ScopedElement e = m_xml.scopedElement("OverallResult"); + e.writeAttribute("success", testCaseStats.totals.assertions.allOk()); + + if (m_config->showDurations() == ShowDurations::Always) + e.writeAttribute("durationInSeconds", m_testCaseTimer.getElapsedSeconds()); + + if (!testCaseStats.stdOut.empty()) + m_xml.scopedElement("StdOut").writeText(trim(testCaseStats.stdOut), XmlFormatting::Newline); + if (!testCaseStats.stdErr.empty()) + m_xml.scopedElement("StdErr").writeText(trim(testCaseStats.stdErr), XmlFormatting::Newline); + + m_xml.endElement(); + } + + void XmlReporter::testGroupEnded(TestGroupStats const &testGroupStats) { + StreamingReporterBase::testGroupEnded(testGroupStats); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testGroupStats.totals.assertions.passed) + .writeAttribute("failures", testGroupStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testGroupStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testGroupStats.totals.testCases.passed) + .writeAttribute("failures", testGroupStats.totals.testCases.failed) + .writeAttribute("expectedFailures", testGroupStats.totals.testCases.failedButOk); + m_xml.endElement(); + } + + void XmlReporter::testRunEnded(TestRunStats const &testRunStats) { + StreamingReporterBase::testRunEnded(testRunStats); + m_xml.scopedElement("OverallResults") + .writeAttribute("successes", testRunStats.totals.assertions.passed) + .writeAttribute("failures", testRunStats.totals.assertions.failed) + .writeAttribute("expectedFailures", testRunStats.totals.assertions.failedButOk); + m_xml.scopedElement("OverallResultsCases") + .writeAttribute("successes", testRunStats.totals.testCases.passed) + .writeAttribute("failures", testRunStats.totals.testCases.failed) + .writeAttribute("expectedFailures", testRunStats.totals.testCases.failedButOk); + m_xml.endElement(); + } + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) + void XmlReporter::benchmarkPreparing(std::string const &name) { + m_xml.startElement("BenchmarkResults").writeAttribute("name", name); + } + + void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) { + m_xml.writeAttribute("samples", info.samples) + .writeAttribute("resamples", info.resamples) + .writeAttribute("iterations", info.iterations) + .writeAttribute("clockResolution", info.clockResolution) + .writeAttribute("estimatedDuration", info.estimatedDuration) + .writeComment("All values in nano seconds"); + } + + void XmlReporter::benchmarkEnded(BenchmarkStats<> const &benchmarkStats) { + m_xml.startElement("mean") + .writeAttribute("value", benchmarkStats.mean.point.count()) + .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.mean.confidence_interval); + m_xml.endElement(); + m_xml.startElement("standardDeviation") + .writeAttribute("value", benchmarkStats.standardDeviation.point.count()) + .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count()) + .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count()) + .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval); + m_xml.endElement(); + m_xml.startElement("outliers") + .writeAttribute("variance", benchmarkStats.outlierVariance) + .writeAttribute("lowMild", benchmarkStats.outliers.low_mild) + .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe) + .writeAttribute("highMild", benchmarkStats.outliers.high_mild) + .writeAttribute("highSevere", benchmarkStats.outliers.high_severe); + m_xml.endElement(); + m_xml.endElement(); + } + + void XmlReporter::benchmarkFailed(std::string const &error) { + m_xml.scopedElement("failed").writeAttribute("message", error); + m_xml.endElement(); + } +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING + + CATCH_REGISTER_REPORTER("xml", XmlReporter) } // end namespace Catch @@ -12724,7 +17532,7 @@ namespace Catch { // end catch_reporter_xml.cpp namespace Catch { - LeakDetector leakDetector; + LeakDetector leakDetector; } #ifdef __clang__ @@ -12739,33 +17547,33 @@ namespace Catch { #ifndef __OBJC__ -#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) +#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN) // Standard C/C++ Win32 Unicode wmain entry point -extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) { +extern "C" int wmain(int argc, wchar_t *argv[], wchar_t *[]) { #else // Standard C/C++ main entry point -int main (int argc, char * argv[]) { +int main(int argc, char *argv[]) { #endif - return Catch::Session().run( argc, argv ); + return Catch::Session().run(argc, argv); } #else // __OBJC__ // Objective-C entry point -int main (int argc, char * const argv[]) { +int main(int argc, char *const argv[]) { #if !CATCH_ARC_ENABLED - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; #endif - Catch::registerTestMethods(); - int result = Catch::Session().run( argc, (char**)argv ); + Catch::registerTestMethods(); + int result = Catch::Session().run(argc, (char **)argv); #if !CATCH_ARC_ENABLED - [pool drain]; + [pool drain]; #endif - return result; + return result; } #endif // __OBJC__ @@ -12776,7 +17584,7 @@ int main (int argc, char * const argv[]) { #if !defined(CATCH_CONFIG_IMPL_ONLY) #ifdef CLARA_CONFIG_MAIN_NOT_DEFINED -# undef CLARA_CONFIG_MAIN +#undef CLARA_CONFIG_MAIN #endif #if !defined(CATCH_CONFIG_DISABLE) @@ -12784,246 +17592,463 @@ int main (int argc, char * const argv[]) { // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define CATCH_REQUIRE(...) INTERNAL_CATCH_TEST("CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_REQUIRE_FALSE(...) \ + INTERNAL_CATCH_TEST("CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__) -#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", __VA_ARGS__ ) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define CATCH_REQUIRE_THROWS(...) INTERNAL_CATCH_THROWS("CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr) +#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) -#endif// CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) - -#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) - -#define CATCH_CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", __VA_ARGS__ ) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) -#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CATCH_REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_REQUIRE_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__) + +#define CATCH_CHECK(...) INTERNAL_CATCH_TEST("CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECK_FALSE(...) \ + INTERNAL_CATCH_TEST("CATCH_CHECK_FALSE", \ + Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, \ + __VA_ARGS__) +#define CATCH_CHECKED_IF(...) INTERNAL_CATCH_IF("CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) \ + INTERNAL_CATCH_ELSE("CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECK_NOFAIL(...) \ + INTERNAL_CATCH_TEST("CATCH_CHECK_NOFAIL", \ + Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, \ + __VA_ARGS__) + +#define CATCH_CHECK_THROWS(...) \ + INTERNAL_CATCH_THROWS("CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr) +#define CATCH_CHECK_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("CATCH_CHECK_THROWS_MATCHES", \ + exceptionType, \ + Catch::ResultDisposition::ContinueOnFailure, \ + matcher, \ + expr) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW(...) \ + INTERNAL_CATCH_NO_THROW("CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) +#define CATCH_CHECK_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg) -#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#define CATCH_REQUIRE_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) -#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) +#define CATCH_INFO(msg) INTERNAL_CATCH_INFO("CATCH_INFO", msg) +#define CATCH_UNSCOPED_INFO(msg) INTERNAL_CATCH_UNSCOPED_INFO("CATCH_UNSCOPED_INFO", msg) +#define CATCH_WARN(msg) \ + INTERNAL_CATCH_MSG("CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg) +#define CATCH_CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__) -#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) -#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) -#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) -#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) -#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define CATCH_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define CATCH_REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define CATCH_SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define CATCH_DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define CATCH_FAIL(...) \ + INTERNAL_CATCH_MSG("CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__) +#define CATCH_FAIL_CHECK(...) \ + INTERNAL_CATCH_MSG("CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CATCH_SUCCEED(...) \ + INTERNAL_CATCH_MSG("CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) #define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#else +#define CATCH_TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, __VA_ARGS__)) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define CATCH_STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + CATCH_SUCCEED(#__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + CATCH_SUCCEED(#__VA_ARGS__) +#else +#define CATCH_STATIC_REQUIRE(...) CATCH_REQUIRE(__VA_ARGS__) +#define CATCH_STATIC_REQUIRE_FALSE(...) CATCH_REQUIRE_FALSE(__VA_ARGS__) +#endif + // "BDD-style" convenience wrappers -#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) -#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc ) -#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc ) -#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) -#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc ) -#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc ) +#define CATCH_SCENARIO(...) CATCH_TEST_CASE("Scenario: " __VA_ARGS__) +#define CATCH_SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) +#define CATCH_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define CATCH_AND_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define CATCH_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define CATCH_AND_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define CATCH_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define CATCH_AND_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define CATCH_BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), \ + INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ + INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) +#define CATCH_BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) +#define REQUIRE(...) INTERNAL_CATCH_TEST("REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define REQUIRE_FALSE(...) \ + INTERNAL_CATCH_TEST("REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__) -#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr ) -#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_THROWS(...) INTERNAL_CATCH_THROWS("REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__) +#define REQUIRE_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr) +#define REQUIRE_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr ) +#define REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ ) +#define REQUIRE_NOTHROW(...) INTERNAL_CATCH_NO_THROW("REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__) -#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ ) -#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ ) +#define CHECK(...) INTERNAL_CATCH_TEST("CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECK_FALSE(...) \ + INTERNAL_CATCH_TEST("CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__) +#define CHECKED_IF(...) INTERNAL_CATCH_IF("CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECKED_ELSE(...) INTERNAL_CATCH_ELSE("CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECK_NOFAIL(...) \ + INTERNAL_CATCH_TEST("CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__) -#define CHECK_THROWS( ... ) INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr ) -#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_THROWS(...) INTERNAL_CATCH_THROWS("CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define CHECK_THROWS_AS(expr, exceptionType) \ + INTERNAL_CATCH_THROWS_AS("CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr) +#define CHECK_THROWS_WITH(expr, matcher) \ + INTERNAL_CATCH_THROWS_STR_MATCHES("CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr ) +#define CHECK_THROWS_MATCHES(expr, exceptionType, matcher) \ + INTERNAL_CATCH_THROWS_MATCHES("CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define CHECK_NOTHROW(...) INTERNAL_CATCH_NO_THROW("CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) +#define CHECK_THAT(arg, matcher) \ + INTERNAL_CHECK_THAT("CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg) -#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) +#define REQUIRE_THAT(arg, matcher) INTERNAL_CHECK_THAT("REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) -#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) -#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << ::Catch::Detail::stringify(msg) ) +#define INFO(msg) INTERNAL_CATCH_INFO("INFO", msg) +#define UNSCOPED_INFO(msg) INTERNAL_CATCH_UNSCOPED_INFO("UNSCOPED_INFO", msg) +#define WARN(msg) INTERNAL_CATCH_MSG("WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg) +#define CAPTURE(...) INTERNAL_CATCH_CAPTURE(INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__) -#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) -#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) -#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) -#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) -#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) -#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ ) -#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) -#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ ) +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE(__VA_ARGS__) +#define TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, __VA_ARGS__) +#define METHOD_AS_TEST_CASE(method, ...) INTERNAL_CATCH_METHOD_AS_TEST_CASE(method, __VA_ARGS__) +#define REGISTER_TEST_CASE(Function, ...) INTERNAL_CATCH_REGISTER_TESTCASE(Function, __VA_ARGS__) +#define SECTION(...) INTERNAL_CATCH_SECTION(__VA_ARGS__) +#define DYNAMIC_SECTION(...) INTERNAL_CATCH_DYNAMIC_SECTION(__VA_ARGS__) +#define FAIL(...) INTERNAL_CATCH_MSG("FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__) +#define FAIL_CHECK(...) \ + INTERNAL_CATCH_MSG("FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) +#define SUCCEED(...) \ + INTERNAL_CATCH_MSG("SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__) #define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE() +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, __VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_LIST_TEST_CASE_METHOD(className, ...) INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(__VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, __VA_ARGS__)) +#define TEMPLATE_LIST_TEST_CASE(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(__VA_ARGS__)) +#define TEMPLATE_LIST_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(className, __VA_ARGS__)) +#endif + +#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) +#define STATIC_REQUIRE(...) \ + static_assert(__VA_ARGS__, #__VA_ARGS__); \ + SUCCEED(#__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) \ + static_assert(!(__VA_ARGS__), "!(" #__VA_ARGS__ ")"); \ + SUCCEED("!(" #__VA_ARGS__ ")") +#else +#define STATIC_REQUIRE(...) REQUIRE(__VA_ARGS__) +#define STATIC_REQUIRE_FALSE(...) REQUIRE_FALSE(__VA_ARGS__) +#endif + #endif -#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) +#define CATCH_TRANSLATE_EXCEPTION(signature) INTERNAL_CATCH_TRANSLATE_EXCEPTION(signature) // "BDD-style" convenience wrappers -#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) -#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#define SCENARIO(...) TEST_CASE("Scenario: " __VA_ARGS__) +#define SCENARIO_METHOD(className, ...) INTERNAL_CATCH_TEST_CASE_METHOD(className, "Scenario: " __VA_ARGS__) -#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc ) -#define WHEN( desc ) SECTION( std::string(" When: ") + desc ) -#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc ) -#define THEN( desc ) SECTION( std::string(" Then: ") + desc ) -#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc ) +#define GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Given: " << desc) +#define AND_GIVEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION("And given: " << desc) +#define WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" When: " << desc) +#define AND_WHEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And when: " << desc) +#define THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" Then: " << desc) +#define AND_THEN(desc) INTERNAL_CATCH_DYNAMIC_SECTION(" And: " << desc) + +#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING) +#define BENCHMARK(...) \ + INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), \ + INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__, , ), \ + INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__, , )) +#define BENCHMARK_ADVANCED(name) \ + INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name) +#endif // CATCH_CONFIG_ENABLE_BENCHMARKING using Catch::Detail::Approx; -#else +#else // CATCH_CONFIG_DISABLE + ////// // If this config identifier is defined then all CATCH macros are prefixed with CATCH_ #ifdef CATCH_CONFIG_PREFIX_ALL -#define CATCH_REQUIRE( ... ) (void)(0) -#define CATCH_REQUIRE_FALSE( ... ) (void)(0) +#define CATCH_REQUIRE(...) (void)(0) +#define CATCH_REQUIRE_FALSE(...) (void)(0) -#define CATCH_REQUIRE_THROWS( ... ) (void)(0) -#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) -#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#define CATCH_REQUIRE_THROWS(...) (void)(0) +#define CATCH_REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_REQUIRE_THROWS_WITH(expr, matcher) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) -#endif// CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0) - -#define CATCH_CHECK( ... ) (void)(0) -#define CATCH_CHECK_FALSE( ... ) (void)(0) -#define CATCH_CHECKED_IF( ... ) if (__VA_ARGS__) -#define CATCH_CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) -#define CATCH_CHECK_NOFAIL( ... ) (void)(0) - -#define CATCH_CHECK_THROWS( ... ) (void)(0) -#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0) -#define CATCH_CHECK_THROWS_WITH( expr, matcher ) (void)(0) -#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#define CATCH_REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_CHECK_NOTHROW( ... ) (void)(0) +#define CATCH_REQUIRE_NOTHROW(...) (void)(0) + +#define CATCH_CHECK(...) (void)(0) +#define CATCH_CHECK_FALSE(...) (void)(0) +#define CATCH_CHECKED_IF(...) if (__VA_ARGS__) +#define CATCH_CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CATCH_CHECK_NOFAIL(...) (void)(0) + +#define CATCH_CHECK_THROWS(...) (void)(0) +#define CATCH_CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CATCH_CHECK_THROWS_WITH(expr, matcher) (void)(0) +#if !defined(CATCH_CONFIG_DISABLE_MATCHERS) +#define CATCH_CHECK_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) +#endif // CATCH_CONFIG_DISABLE_MATCHERS +#define CATCH_CHECK_NOTHROW(...) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CATCH_CHECK_THAT( arg, matcher ) (void)(0) +#define CATCH_CHECK_THAT(arg, matcher) (void)(0) -#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) +#define CATCH_REQUIRE_THAT(arg, matcher) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CATCH_INFO( msg ) (void)(0) -#define CATCH_WARN( msg ) (void)(0) -#define CATCH_CAPTURE( msg ) (void)(0) +#define CATCH_INFO(msg) (void)(0) +#define CATCH_UNSCOPED_INFO(msg) (void)(0) +#define CATCH_WARN(msg) (void)(0) +#define CATCH_CAPTURE(msg) (void)(0) -#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -#define CATCH_METHOD_AS_TEST_CASE( method, ... ) -#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0) -#define CATCH_SECTION( ... ) -#define CATCH_FAIL( ... ) (void)(0) -#define CATCH_FAIL_CHECK( ... ) (void)(0) -#define CATCH_SUCCEED( ... ) (void)(0) +#define CATCH_TEST_CASE(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define CATCH_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define CATCH_METHOD_AS_TEST_CASE(method, ...) +#define CATCH_REGISTER_TEST_CASE(Function, ...) (void)(0) +#define CATCH_SECTION(...) +#define CATCH_DYNAMIC_SECTION(...) +#define CATCH_FAIL(...) (void)(0) +#define CATCH_FAIL_CHECK(...) (void)(0) +#define CATCH_SUCCEED(...) (void)(0) -#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define CATCH_TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define CATCH_TEMPLATE_TEST_CASE(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)) +#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__)) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE(...) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(...) CATCH_TEMPLATE_TEST_CASE(__VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) CATCH_TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#endif // "BDD-style" convenience wrappers -#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) -#define CATCH_GIVEN( desc ) -#define CATCH_WHEN( desc ) -#define CATCH_AND_WHEN( desc ) -#define CATCH_THEN( desc ) -#define CATCH_AND_THEN( desc ) +#define CATCH_SCENARIO(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define CATCH_SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), className) +#define CATCH_GIVEN(desc) +#define CATCH_AND_GIVEN(desc) +#define CATCH_WHEN(desc) +#define CATCH_AND_WHEN(desc) +#define CATCH_THEN(desc) +#define CATCH_AND_THEN(desc) + +#define CATCH_STATIC_REQUIRE(...) (void)(0) +#define CATCH_STATIC_REQUIRE_FALSE(...) (void)(0) // If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required #else -#define REQUIRE( ... ) (void)(0) -#define REQUIRE_FALSE( ... ) (void)(0) +#define REQUIRE(...) (void)(0) +#define REQUIRE_FALSE(...) (void)(0) -#define REQUIRE_THROWS( ... ) (void)(0) -#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0) -#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) +#define REQUIRE_THROWS(...) (void)(0) +#define REQUIRE_THROWS_AS(expr, exceptionType) (void)(0) +#define REQUIRE_THROWS_WITH(expr, matcher) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#define REQUIRE_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define REQUIRE_NOTHROW( ... ) (void)(0) +#define REQUIRE_NOTHROW(...) (void)(0) -#define CHECK( ... ) (void)(0) -#define CHECK_FALSE( ... ) (void)(0) -#define CHECKED_IF( ... ) if (__VA_ARGS__) -#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__)) -#define CHECK_NOFAIL( ... ) (void)(0) +#define CHECK(...) (void)(0) +#define CHECK_FALSE(...) (void)(0) +#define CHECKED_IF(...) if (__VA_ARGS__) +#define CHECKED_ELSE(...) if (!(__VA_ARGS__)) +#define CHECK_NOFAIL(...) (void)(0) -#define CHECK_THROWS( ... ) (void)(0) -#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0) -#define CHECK_THROWS_WITH( expr, matcher ) (void)(0) +#define CHECK_THROWS(...) (void)(0) +#define CHECK_THROWS_AS(expr, exceptionType) (void)(0) +#define CHECK_THROWS_WITH(expr, matcher) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0) +#define CHECK_THROWS_MATCHES(expr, exceptionType, matcher) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define CHECK_NOTHROW( ... ) (void)(0) +#define CHECK_NOTHROW(...) (void)(0) #if !defined(CATCH_CONFIG_DISABLE_MATCHERS) -#define CHECK_THAT( arg, matcher ) (void)(0) +#define CHECK_THAT(arg, matcher) (void)(0) -#define REQUIRE_THAT( arg, matcher ) (void)(0) +#define REQUIRE_THAT(arg, matcher) (void)(0) #endif // CATCH_CONFIG_DISABLE_MATCHERS -#define INFO( msg ) (void)(0) -#define WARN( msg ) (void)(0) -#define CAPTURE( msg ) (void)(0) +#define INFO(msg) (void)(0) +#define UNSCOPED_INFO(msg) (void)(0) +#define WARN(msg) (void)(0) +#define CAPTURE(msg) (void)(0) -#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) -#define METHOD_AS_TEST_CASE( method, ... ) -#define REGISTER_TEST_CASE( Function, ... ) (void)(0) -#define SECTION( ... ) -#define FAIL( ... ) (void)(0) -#define FAIL_CHECK( ... ) (void)(0) -#define SUCCEED( ... ) (void)(0) -#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )) +#define TEST_CASE(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define METHOD_AS_TEST_CASE(method, ...) +#define REGISTER_TEST_CASE(Function, ...) (void)(0) +#define SECTION(...) +#define DYNAMIC_SECTION(...) +#define FAIL(...) (void)(0) +#define FAIL_CHECK(...) (void)(0) +#define SUCCEED(...) (void)(0) +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_SIG(...) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#else +#define TEMPLATE_TEST_CASE(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_SIG(...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)) +#define TEMPLATE_TEST_CASE_METHOD_SIG(className, ...) \ + INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__)) +#define TEMPLATE_PRODUCT_TEST_CASE(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_SIG(...) TEMPLATE_TEST_CASE(__VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD(className, ...) TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(className, ...) TEMPLATE_TEST_CASE_METHOD(className, __VA_ARGS__) +#endif + +#define STATIC_REQUIRE(...) (void)(0) +#define STATIC_REQUIRE_FALSE(...) (void)(0) #endif -#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) +#define CATCH_TRANSLATE_EXCEPTION(signature) \ + INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG(INTERNAL_CATCH_UNIQUE_NAME(catch_internal_ExceptionTranslator), signature) // "BDD-style" convenience wrappers -#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) ) -#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className ) +#define SCENARIO(...) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____)) +#define SCENARIO_METHOD(className, ...) \ + INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____T_E_S_T____), className) -#define GIVEN( desc ) -#define WHEN( desc ) -#define AND_WHEN( desc ) -#define THEN( desc ) -#define AND_THEN( desc ) +#define GIVEN(desc) +#define AND_GIVEN(desc) +#define WHEN(desc) +#define AND_WHEN(desc) +#define THEN(desc) +#define AND_THEN(desc) using Catch::Detail::Approx; @@ -13033,18 +18058,16 @@ using Catch::Detail::Approx; // start catch_reenable_warnings.h - #ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(pop) -# else -# pragma clang diagnostic pop -# endif +#ifdef __ICC // icpc defines the __clang__ macro +#pragma warning(pop) +#else +#pragma clang diagnostic pop +#endif #elif defined __GNUC__ -# pragma GCC diagnostic pop +#pragma GCC diagnostic pop #endif // end catch_reenable_warnings.h // end catch.hpp #endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED - diff --git a/unittests/clone_object.chai b/unittests/clone_object.chai new file mode 100644 index 00000000..8864d0ce --- /dev/null +++ b/unittests/clone_object.chai @@ -0,0 +1,41 @@ + +/* +global clone_count = 0; + +class Cloneable +{ + def Cloneable() { + } + +} + + +def clone(Cloneable c) +{ + print("Clone called"); + ++clone_count; + return c; +} + + +class MyObject +{ + def MyObject() { + this.data = Cloneable(); + } + + var data; +} + + +assert_equal(0, clone_count); + +var o = MyObject(); + +assert_equal(0, clone_count); + +var p = o; + +assert_equal(1, clone_count); +*/ + diff --git a/unittests/compiled_tests.cpp b/unittests/compiled_tests.cpp index 8723d23d..f8873df8 100644 --- a/unittests/compiled_tests.cpp +++ b/unittests/compiled_tests.cpp @@ -1,13 +1,11 @@ // All of these are necessary because of catch.hpp. It's OK, they'll be // caught in other cpp files if chaiscript causes them - #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4062 4242 4566 4640 4702 6330 28251) #endif - #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" @@ -16,17 +14,14 @@ #pragma GCC diagnostic ignored "-Wignored-qualifiers" #endif - #include #include -#include #include +#include #include "../static_libs/chaiscript_parser.hpp" #include "../static_libs/chaiscript_stdlib.hpp" - - #define CATCH_CONFIG_MAIN #include @@ -34,27 +29,43 @@ #include "catch.hpp" // lambda_tests -TEST_CASE("C++11 Lambdas Can Be Registered") -{ - +TEST_CASE("C++11 Lambdas Can Be Registered") { // We cannot deduce the type of a lambda expression, you must either wrap it // in an std::function or provide the signature - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); - chai.add(chaiscript::fun([]()->std::string { return "hello"; } ), "f1"); + chai.add(chaiscript::fun([]() -> std::string { return "hello"; }), "f1"); // wrap - chai.add(chaiscript::fun(std::function([] { return "world"; } )), "f2"); + chai.add(chaiscript::fun(std::function([] { return "world"; })), "f2"); CHECK(chai.eval("f1()") == "hello"); CHECK(chai.eval("f2()") == "world"); } +TEST_CASE("Lambdas can return boolean") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + + // check lambdas returning bool from chaiscript: + std::function chai_function; + + CHECK_NOTHROW(chai_function = chai.eval>("fun() { 42 != 0 }")); + + bool result = false; + CHECK_NOTHROW(result = chai_function()); + + CHECK(result == true); + + // check lambdas returning bool from C++: + auto cpp_function = [](int x) { return x == 42; }; + CHECK_NOTHROW(chai.add(chaiscript::fun(cpp_function), "cpp_function")); + CHECK_NOTHROW(result = chai.eval("cpp_function(314)")); + CHECK(result == false); +} // dynamic_object tests -TEST_CASE("Dynamic_Object attributes can be shared with C++") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Dynamic_Object attributes can be shared with C++") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai("attr bob::z; def bob::bob() { this.z = 10 }; auto x = bob()"); @@ -68,67 +79,51 @@ TEST_CASE("Dynamic_Object attributes can be shared with C++") CHECK(chaiscript::boxed_cast(mydo.get_attr("z")) == 15); - int &z = chaiscript::boxed_cast(mydo.get_attr("z")); + int &z = chaiscript::boxed_cast(mydo.get_attr("z")); CHECK(z == 15); z = 20; - + CHECK(z == 20); CHECK(chaiscript::boxed_cast(chai("x.z")) == 20); } - -TEST_CASE("Function objects can be created from chaiscript functions") -{ - - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Function objects can be created from chaiscript functions") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.eval("def func() { print(\"Hello World\"); } "); - std::function f = chai.eval >("func"); + std::function f = chai.eval>("func"); f(); - CHECK(chai.eval >("to_string")(6) == "6"); - CHECK(chai.eval >("to_string")(chaiscript::var(6)) == "6"); + CHECK(chai.eval>("to_string")(6) == "6"); + CHECK(chai.eval>("to_string")(chaiscript::var(6)) == "6"); } - -TEST_CASE("ChaiScript can be created and destroyed on heap") -{ - auto *chai = new chaiscript::ChaiScript_Basic(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("ChaiScript can be created and destroyed on heap") { + auto *chai = new chaiscript::ChaiScript_Basic(create_chaiscript_stdlib(), create_chaiscript_parser()); delete chai; } - ///////// Arithmetic Conversions // Tests to make sure that type conversions happen only when they should -void arithmetic_conversions_f1(int) -{ -} +void arithmetic_conversions_f1(int) {} -void arithmetic_conversions_f4(std::string) -{ -} +void arithmetic_conversions_f4(std::string) {} -void arithmetic_conversions_f2(int) -{ -} +void arithmetic_conversions_f2(int) {} -void arithmetic_conversions_f3(double) -{ -} +void arithmetic_conversions_f3(double) {} -void arithmetic_conversions_f_func_return(const std::function &f) -{ +void arithmetic_conversions_f_func_return(const std::function &f) { // test the ability to return an unsigned with auto conversion f(4); } -TEST_CASE("Test automatic arithmetic conversions") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test automatic arithmetic conversions") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&arithmetic_conversions_f1), "f1"); chai.add(chaiscript::fun(&arithmetic_conversions_f2), "f2"); @@ -163,13 +158,10 @@ TEST_CASE("Test automatic arithmetic conversions") CHECK_THROWS(chai.eval("f2(1.0l)")); } - /////// Exception handling - -TEST_CASE("Generic exception handling with C++") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Generic exception handling with C++") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); try { chai.eval("throw(runtime_error(\"error\"));"); @@ -180,9 +172,8 @@ TEST_CASE("Generic exception handling with C++") } } -TEST_CASE("Throw an int") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Throw an int") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); try { chai.eval("throw(1)", chaiscript::exception_specification()); @@ -192,9 +183,8 @@ TEST_CASE("Throw an int") } } -TEST_CASE("Throw int or double") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Throw int or double") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); try { chai.eval("throw(1.0)", chaiscript::exception_specification()); @@ -204,12 +194,45 @@ TEST_CASE("Throw int or double") } } -TEST_CASE("Throw a runtime_error") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Deduction of pointer return types") { + int val = 5; + int *val_ptr = &val; + auto &val_ptr_ref = val_ptr; + const auto &val_ptr_const_ref = val_ptr; + + auto get_val_ptr = [&]() -> int * { return val_ptr; }; + auto get_val_const_ptr = [&]() -> int const * { return val_ptr; }; + auto get_val_ptr_ref = [&]() -> int *& { return val_ptr_ref; }; + auto get_val_ptr_const_ref = [&]() -> int *const & { return val_ptr_const_ref; }; + // auto get_val_const_ptr_const_ref = [ref=std::cref(val_ptr)]() -> int const * const & { return ref.get(); }; + + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + chai.add(chaiscript::fun(get_val_ptr), "get_val_ptr"); + chai.add(chaiscript::fun(get_val_const_ptr), "get_val_const_ptr"); + chai.add(chaiscript::fun(get_val_ptr_ref), "get_val_ptr_ref"); + chai.add(chaiscript::fun(get_val_ptr_const_ref), "get_val_ptr_const_ref"); + // chai.add(chaiscript::fun(get_val_const_ptr_const_ref), "get_val_const_ptr_const_ref"); + + CHECK(chai.eval("get_val_ptr()") == &val); + CHECK(*chai.eval("get_val_ptr()") == val); + CHECK(chai.eval("get_val_const_ptr()") == &val); + CHECK(*chai.eval("get_val_const_ptr()") == val); + + // note that we cannot maintain the references here, + // chaiscript internals cannot handle that, effectively pointer to pointer + CHECK(chai.eval("get_val_ptr_ref()") == &val); + CHECK(*chai.eval("get_val_ptr_ref()") == val); + CHECK(chai.eval("get_val_ptr_const_ref()") == &val); + CHECK(*chai.eval("get_val_ptr_const_ref()") == val); + // CHECK(chai.eval("get_val_const_ptr_const_ref()") == &val); +} + +TEST_CASE("Throw a runtime_error") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); try { - chai.eval("throw(runtime_error(\"error\"))", chaiscript::exception_specification()); + chai.eval("throw(runtime_error(\"error\"))", + chaiscript::exception_specification()); REQUIRE(false); } catch (const double) { REQUIRE(false); @@ -224,9 +247,8 @@ TEST_CASE("Throw a runtime_error") } } -TEST_CASE("Throw unhandled type") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Throw unhandled type") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); try { chai.eval("throw(\"error\")", chaiscript::exception_specification()); @@ -244,18 +266,14 @@ TEST_CASE("Throw unhandled type") } } - ///////////// Tests to make sure no arity, dispatch or guard errors leak up past eval - -int expected_eval_errors_test_one(const int &) -{ +int expected_eval_errors_test_one(const int &) { return 1; } -TEST_CASE("No unexpected exceptions leak") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("No unexpected exceptions leak") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&expected_eval_errors_test_one), "test_fun"); chai.eval("def guard_fun(i) : i.get_type_info().is_type_arithmetic() {} "); @@ -273,7 +291,6 @@ TEST_CASE("No unexpected exceptions leak") // guard failure CHECK_THROWS_AS(chai.eval("\"test\".guard_fun()"), chaiscript::exception::eval_error); - // regular notation // non-existent function @@ -288,7 +305,6 @@ TEST_CASE("No unexpected exceptions leak") // guard failure CHECK_THROWS_AS(chai.eval("guard_fun(\"test\")"), chaiscript::exception::eval_error); - // index operator CHECK_THROWS_AS(chai.eval("var a = [1,2,3]; a[\"bob\"];"), chaiscript::exception::eval_error); @@ -297,31 +313,25 @@ TEST_CASE("No unexpected exceptions leak") // binary operator CHECK_THROWS_AS(chai.eval("\"bob\" + 1"), chaiscript::exception::eval_error); - - } - //////// Tests to make sure that the order in which function dispatches occur is correct #include -int function_ordering_test_one(const int &) -{ +int function_ordering_test_one(const int &) { return 1; } -int function_ordering_test_two(int &) -{ +int function_ordering_test_two(int &) { return 2; } -TEST_CASE("Function ordering") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Function ordering") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.eval("def test_fun(x) { return 3; }"); chai.eval("def test_fun(x) : x == \"hi\" { return 4; }"); -// chai.eval("def test_fun(x) { return 5; }"); + // chai.eval("def test_fun(x) { return 5; }"); chai.add(chaiscript::fun(&function_ordering_test_one), "test_fun"); chai.add(chaiscript::fun(&function_ordering_test_two), "test_fun"); @@ -331,18 +341,12 @@ TEST_CASE("Function ordering") CHECK(chai.eval("test_fun(\"hi\")") == 4); } - - - -int functor_cast_test_call(const std::function &f, int val) -{ +int functor_cast_test_call(const std::function &f, int val) { return f(val); } -TEST_CASE("Functor cast") -{ - - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Functor cast") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&functor_cast_test_call), "test_call"); @@ -352,38 +356,32 @@ TEST_CASE("Functor cast") CHECK(d == 3 * 6); } -TEST_CASE("Non-ASCII characters in the middle of string") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Non-ASCII characters in the middle of string") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); CHECK_THROWS_AS(chai.eval("prin\xeft \"Hello World\""), chaiscript::exception::eval_error); } -TEST_CASE("Non-ASCII characters in the beginning of string") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); - CHECK_THROWS_AS(chai.eval("\xefprint \"Hello World\""), chaiscript::exception::eval_error); +TEST_CASE("Non-ASCII characters in the beginning of string") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + CHECK_THROWS_AS(chai.eval("\xefprint \"Hello World\""), chaiscript::exception::eval_error); } -TEST_CASE("Non-ASCII characters in the end of string") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); - CHECK_THROWS_AS(chai.eval("print \"Hello World\"\xef"), chaiscript::exception::eval_error); +TEST_CASE("Non-ASCII characters in the end of string") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + CHECK_THROWS_AS(chai.eval("print \"Hello World\"\xef"), chaiscript::exception::eval_error); } -TEST_CASE("BOM in string") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); - CHECK_THROWS_AS(chai.eval("\xef\xbb\xbfprint \"Hello World\""), chaiscript::exception::eval_error); +TEST_CASE("BOM in string") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + CHECK_THROWS_AS(chai.eval("\xef\xbb\xbfprint \"Hello World\""), chaiscript::exception::eval_error); } -int set_state_test_myfun() -{ +int set_state_test_myfun() { return 2; } -TEST_CASE("Set and restore chai state") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Set and restore chai state") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); // save the initial state of globals and locals auto firststate = chai.get_state(); @@ -394,7 +392,6 @@ TEST_CASE("Set and restore chai state") chai.add(chaiscript::fun(&set_state_test_myfun), "myfun"); - CHECK(chai.eval("myfun()") == 2); CHECK(chai.eval("i") == 1); @@ -415,21 +412,21 @@ TEST_CASE("Set and restore chai state") CHECK_THROWS_AS(chai.eval("i"), chaiscript::exception::eval_error); } - //// Short comparisons class Short_Comparison_Test { - public: - Short_Comparison_Test() : value_(5) {} +public: + Short_Comparison_Test() + : value_(5) { + } short get_value() const { return value_; } short value_; }; -TEST_CASE("Short comparison with int") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Short comparison with int") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::user_type(), "Test"); chai.add(chaiscript::constructor(), "Test"); @@ -440,19 +437,13 @@ TEST_CASE("Short comparison with int") CHECK(chai.eval("t.get_value() == 5")); } - - - ///// Test lookup of type names - -class Type_Name_MyClass -{ +class Type_Name_MyClass { }; -TEST_CASE("Test lookup of type names") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test lookup of type names") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); auto type = chaiscript::user_type(); chai.add(type, "MyClass"); @@ -460,31 +451,23 @@ TEST_CASE("Test lookup of type names") CHECK(chai.get_type_name() == "MyClass"); } +/////// make sure many chaiscript objects can exist simultaneously -/////// make sure many chaiscript objects can exist simultaneously - - -int simultaneous_chaiscript_do_something(int i) -{ +int simultaneous_chaiscript_do_something(int i) { return i + 2; } -int simultaneous_chaiscript_do_something_else(int i) -{ +int simultaneous_chaiscript_do_something_else(int i) { return i * 2; } - - -TEST_CASE("Simultaneous ChaiScript tests") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Simultaneous ChaiScript tests") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&simultaneous_chaiscript_do_something), "do_something"); chai.add(chaiscript::var(1), "i"); - for (int i = 0; i < 10; ++i) - { - chaiscript::ChaiScript_Basic chai2(create_chaiscript_stdlib(),create_chaiscript_parser()); + for (int i = 0; i < 10; ++i) { + chaiscript::ChaiScript_Basic chai2(create_chaiscript_stdlib(), create_chaiscript_parser()); chai2.add(chaiscript::fun(&simultaneous_chaiscript_do_something_else), "do_something_else"); CHECK(chai.eval("do_something(" + std::to_string(i) + ")") == i + 2); @@ -496,87 +479,70 @@ TEST_CASE("Simultaneous ChaiScript tests") } } - - /////////////// test utility functions -class Utility_Test -{ - public: - void function() {} - std::string function2() { return "Function2"; } - void function3() {} - std::string functionOverload(double) { return "double"; } - std::string functionOverload(int) { return "int"; } +class Utility_Test { +public: + void function() {} + std::string function2() { return "Function2"; } + void function3() {} + std::string functionOverload(double) { return "double"; } + std::string functionOverload(int) { return "int"; } }; -TEST_CASE("Utility_Test utility class wrapper") -{ - +TEST_CASE("Utility_Test utility class wrapper") { chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); using namespace chaiscript; /// \todo fix overload resolution for fun<> - chaiscript::utility::add_class(*m, + chaiscript::utility::add_class( + *m, "Utility_Test", - { constructor(), - constructor() }, - { {fun(&Utility_Test::function), "function"}, - {fun(&Utility_Test::function2), "function2"}, - {fun(&Utility_Test::function3), "function3"}, - {fun(static_cast(&Utility_Test::functionOverload)), "functionOverload" }, - {fun(static_cast(&Utility_Test::functionOverload)), "functionOverload" }, - {fun(static_cast(&Utility_Test::operator=)), "=" } - - } - ); + {constructor(), constructor()}, + {{fun(&Utility_Test::function), "function"}, + {fun(&Utility_Test::function2), "function2"}, + {fun(&Utility_Test::function3), "function3"}, + {fun(static_cast(&Utility_Test::functionOverload)), "functionOverload"}, + {fun(static_cast(&Utility_Test::functionOverload)), "functionOverload"}, + {fun(static_cast(&Utility_Test::operator=)), "="} + }); - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("auto t = Utility_Test(); t.function2(); ") == "Function2"); CHECK(chai.eval("auto t2 = Utility_Test(); t2.functionOverload(1); ") == "int"); CHECK(chai.eval("auto t3 = Utility_Test(); t3.functionOverload(1.1); ") == "double"); chai.eval("t = Utility_Test();"); - } - -enum Utility_Test_Numbers -{ +enum Utility_Test_Numbers { ONE, TWO, THREE }; -void do_something_with_enum_vector(const std::vector &v) -{ +void do_something_with_enum_vector(const std::vector &v) { CHECK(v.size() == 3); CHECK(v[0] == ONE); CHECK(v[1] == THREE); CHECK(v[2] == TWO); } -TEST_CASE("Utility_Test utility class wrapper for enum") -{ - +TEST_CASE("Utility_Test utility class wrapper for enum") { chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); using namespace chaiscript; chaiscript::utility::add_class(*m, - "Utility_Test_Numbers", - { { ONE, "ONE" }, - { TWO, "TWO" }, - { THREE, "THREE" } + "Utility_Test_Numbers", + {{ONE, "ONE"}, {TWO, "TWO"}, {THREE, "THREE"} - } - ); + }); - - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("ONE ") == 0); @@ -598,72 +564,58 @@ TEST_CASE("Utility_Test utility class wrapper for enum") CHECK(chai.eval("ONE == ONE")); CHECK(chai.eval("ONE != TWO")); CHECK_NOTHROW(chai.eval("var o = ONE; o = TWO")); - - } - ////// Object copy count test -class Object_Copy_Count_Test -{ - public: - Object_Copy_Count_Test() - { - std::cout << "Object_Copy_Count_Test()\n"; - ++constructcount(); - } +class Object_Copy_Count_Test { +public: + Object_Copy_Count_Test() { + std::cout << "Object_Copy_Count_Test()\n"; + ++constructcount(); + } - Object_Copy_Count_Test(const Object_Copy_Count_Test &) - { - std::cout << "Object_Copy_Count_Test(const Object_Copy_Count_Test &)\n"; - ++copycount(); - } + Object_Copy_Count_Test(const Object_Copy_Count_Test &) { + std::cout << "Object_Copy_Count_Test(const Object_Copy_Count_Test &)\n"; + ++copycount(); + } - Object_Copy_Count_Test(Object_Copy_Count_Test &&) - { - std::cout << "Object_Copy_Count_Test(Object_Copy_Count_Test &&)\n"; - ++movecount(); - } + Object_Copy_Count_Test(Object_Copy_Count_Test &&) { + std::cout << "Object_Copy_Count_Test(Object_Copy_Count_Test &&)\n"; + ++movecount(); + } - ~Object_Copy_Count_Test() - { - std::cout << "~Object_Copy_Count_Test()\n"; - ++destructcount(); - } + ~Object_Copy_Count_Test() { + std::cout << "~Object_Copy_Count_Test()\n"; + ++destructcount(); + } - static int& constructcount() - { - static int c = 0; - return c; - } + static int &constructcount() { + static int c = 0; + return c; + } - static int& copycount() - { - static int c = 0; - return c; - } + static int ©count() { + static int c = 0; + return c; + } - static int& movecount() - { - static int c = 0; - return c; - } + static int &movecount() { + static int c = 0; + return c; + } - static int& destructcount() - { - static int c = 0; - return c; - } + static int &destructcount() { + static int c = 0; + return c; + } }; -Object_Copy_Count_Test object_copy_count_create() -{ +Object_Copy_Count_Test object_copy_count_create() { return Object_Copy_Count_Test(); } -TEST_CASE("Object copy counts") -{ +TEST_CASE("Object copy counts") { chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); m->add(chaiscript::user_type(), "Object_Copy_Count_Test"); @@ -671,55 +623,34 @@ TEST_CASE("Object copy counts") m->add(chaiscript::constructor(), "Object_Copy_Count_Test"); m->add(chaiscript::fun(&object_copy_count_create), "create"); - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(m); chai.eval(" { auto i = create(); } "); CHECK(Object_Copy_Count_Test::copycount() == 0); CHECK(Object_Copy_Count_Test::constructcount() == 1); - - -#ifdef CHAISCRIPT_MSVC - CHECK(Object_Copy_Count_Test::destructcount() == 3); - CHECK(Object_Copy_Count_Test::movecount() == 2); -#else CHECK(Object_Copy_Count_Test::destructcount() == 2); CHECK(Object_Copy_Count_Test::movecount() == 1); -#endif } - ///////////////////// Object lifetime test 1 +class Object_Lifetime_Test { +public: + Object_Lifetime_Test() { ++count(); } -class Object_Lifetime_Test -{ - public: - Object_Lifetime_Test() - { - ++count(); - } + Object_Lifetime_Test(const Object_Lifetime_Test &) { ++count(); } - Object_Lifetime_Test(const Object_Lifetime_Test &) - { - ++count(); - } + ~Object_Lifetime_Test() { --count(); } - ~Object_Lifetime_Test() - { - --count(); - } - - static int& count() - { - static int c = 0; - return c; - } + static int &count() { + static int c = 0; + return c; + } }; -TEST_CASE("Object lifetime tests") -{ +TEST_CASE("Object lifetime tests") { chaiscript::ModulePtr m = chaiscript::ModulePtr(new chaiscript::Module()); m->add(chaiscript::user_type(), "Object_Lifetime_Test"); @@ -727,7 +658,7 @@ TEST_CASE("Object lifetime tests") m->add(chaiscript::constructor(), "Object_Lifetime_Test"); m->add(chaiscript::fun(&Object_Lifetime_Test::count), "count"); - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(m); CHECK(chai.eval("count()") == 0); @@ -738,60 +669,58 @@ TEST_CASE("Object lifetime tests") CHECK(chai.eval("i = 0; { auto t = Object_Lifetime_Test(); { auto t2 = Object_Lifetime_Test(); } } i = count(); return i;") == 0); } - - //// Object lifetime tests 2 template -struct Object_Lifetime_Vector2 -{ - Object_Lifetime_Vector2() : x(0), y(0) {} - Object_Lifetime_Vector2(T px, T py) : x(px), y(py) {} - Object_Lifetime_Vector2(const Object_Lifetime_Vector2& cp) : x(cp.x), y(cp.y) {} +struct Object_Lifetime_Vector2 { + Object_Lifetime_Vector2() + : x(0) + , y(0) { + } + Object_Lifetime_Vector2(T px, T py) + : x(px) + , y(py) { + } + Object_Lifetime_Vector2(const Object_Lifetime_Vector2 &cp) noexcept + : x(cp.x) + , y(cp.y) { + } - Object_Lifetime_Vector2& operator+=(const Object_Lifetime_Vector2& vec_r) - { + Object_Lifetime_Vector2 &operator+=(const Object_Lifetime_Vector2 &vec_r) { x += vec_r.x; y += vec_r.y; return *this; } - Object_Lifetime_Vector2 operator+(const Object_Lifetime_Vector2& vec_r) - { - return Object_Lifetime_Vector2(*this += vec_r); - } + Object_Lifetime_Vector2 operator+(const Object_Lifetime_Vector2 &vec_r) { return Object_Lifetime_Vector2(*this += vec_r); } - Object_Lifetime_Vector2 &operator=(const Object_Lifetime_Vector2& ver_r) - { + Object_Lifetime_Vector2 &operator=(const Object_Lifetime_Vector2 &ver_r) { x = ver_r.x; y = ver_r.y; return *this; } - T x; T y; }; -Object_Lifetime_Vector2 Object_Lifetime_Vector2_GetValue() -{ - return Object_Lifetime_Vector2(10,15); +Object_Lifetime_Vector2 Object_Lifetime_Vector2_GetValue() { + return Object_Lifetime_Vector2(10, 15); } -TEST_CASE("Object lifetime test 2") -{ - chaiscript::ChaiScript_Basic _script(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Object lifetime test 2") { + chaiscript::ChaiScript_Basic _script(create_chaiscript_stdlib(), create_chaiscript_parser()); - //Registering stuff + // Registering stuff _script.add(chaiscript::user_type>(), "Object_Lifetime_Vector2f"); - _script.add(chaiscript::constructor ()>(), "Object_Lifetime_Vector2f"); - _script.add(chaiscript::constructor (float, float)>(), "Object_Lifetime_Vector2f"); - _script.add(chaiscript::constructor (const Object_Lifetime_Vector2&)>(), "Object_Lifetime_Vector2f"); + _script.add(chaiscript::constructor()>(), "Object_Lifetime_Vector2f"); + _script.add(chaiscript::constructor(float, float)>(), "Object_Lifetime_Vector2f"); + _script.add(chaiscript::constructor(const Object_Lifetime_Vector2 &)>(), "Object_Lifetime_Vector2f"); _script.add(chaiscript::fun(&Object_Lifetime_Vector2::x), "x"); _script.add(chaiscript::fun(&Object_Lifetime_Vector2::y), "y"); - _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator +), "+"); - _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator +=), "+="); - _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator =), "="); + _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator+), "+"); + _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator+=), "+="); + _script.add(chaiscript::fun(&Object_Lifetime_Vector2::operator=), "="); _script.add(chaiscript::fun(&Object_Lifetime_Vector2_GetValue), "getValue"); _script.eval(R"( @@ -805,23 +734,19 @@ TEST_CASE("Object lifetime test 2") CHECK(_script.eval("to_string(test)") == "10"); CHECK(_script.eval("to_string(test2.x)") == "10"); - } - - - ///// Non-polymorphic base class conversions -class Non_Poly_Base {}; -class Non_Poly_Derived : public Non_Poly_Base {}; -int myfunction(Non_Poly_Base *) -{ +class Non_Poly_Base { +}; +class Non_Poly_Derived : public Non_Poly_Base { +}; +int myfunction(Non_Poly_Base *) { return 2; } -TEST_CASE("Test Derived->Base with non-polymorphic classes") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test Derived->Base with non-polymorphic classes") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::base_class()); Non_Poly_Derived d; chai.add(chaiscript::var(&d), "d"); @@ -829,18 +754,12 @@ TEST_CASE("Test Derived->Base with non-polymorphic classes") CHECK(chai.eval("myfunction(d)") == 2); } - -struct TestCppVariableScope -{ - void print() - { - std::cout << "Printed" << std::endl; - } +struct TestCppVariableScope { + void print() { std::cout << "Printed" << std::endl; } }; -TEST_CASE("Variable Scope When Calling From C++") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Variable Scope When Calling From C++") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::user_type(), "Test"); chai.add(chaiscript::constructor(), "Test"); chai.add(chaiscript::fun(&TestCppVariableScope::print), "print"); @@ -861,9 +780,8 @@ TEST_CASE("Variable Scope When Calling From C++") CHECK_THROWS(func()); } -TEST_CASE("Variable Scope When Calling From C++ 2") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Variable Scope When Calling From C++ 2") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.eval("var obj = 2;"); auto func = chai.eval>("fun(){ return obj; }"); CHECK_THROWS(func()); @@ -873,36 +791,29 @@ void ulonglong(unsigned long long i) { std::cout << i << '\n'; } - void longlong(long long i) { std::cout << i << '\n'; } -TEST_CASE("Test long long dispatch") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test long long dispatch") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&longlong), "longlong"); chai.add(chaiscript::fun(&ulonglong), "ulonglong"); chai.eval("longlong(15)"); chai.eval("ulonglong(15)"); } - -struct Returned_Converted_Config -{ +struct Returned_Converted_Config { int num_iterations; int something_else; std::string a_string; - std::function a_function; + std::function a_function; }; +TEST_CASE("Return of converted type from script") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); - -TEST_CASE("Return of converted type from script") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); - - chai.add(chaiscript::constructor(), "Returned_Converted_Config"); + chai.add(chaiscript::constructor(), "Returned_Converted_Config"); chai.add(chaiscript::fun(&Returned_Converted_Config::num_iterations), "num_iterations"); chai.add(chaiscript::fun(&Returned_Converted_Config::something_else), "something_else"); chai.add(chaiscript::fun(&Returned_Converted_Config::a_string), "a_string"); @@ -926,7 +837,6 @@ TEST_CASE("Return of converted type from script") )"); - std::cout << typeid(decltype(c)).name() << std::endl; std::cout << "Info: " << c.size() << " " << &c[0] << std::endl; @@ -939,16 +849,12 @@ TEST_CASE("Return of converted type from script") chai.add(chaiscript::user_type(), "Returned_Converted_Config"); } - -int get_value_a(const std::map &t_m) -{ +int get_value_a(const std::map &t_m) { return t_m.at("a"); } - -TEST_CASE("Map conversions") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Map conversions") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::map_conversion>()); chai.add(chaiscript::fun(&get_value_a), "get_value_a"); @@ -960,46 +866,39 @@ TEST_CASE("Map conversions") CHECK(c == 42); } - -TEST_CASE("Parse floats with non-posix locale") -{ +TEST_CASE("Parse floats with non-posix locale") { #ifdef CHAISCRIPT_MSVC std::setlocale(LC_ALL, "en-ZA"); #else std::setlocale(LC_ALL, "en_ZA.utf8"); #endif - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); const double parsed = chai.eval("print(1.3); 1.3"); CHECK(parsed == Approx(1.3)); const std::string str = chai.eval("to_string(1.3)"); CHECK(str == "1.3"); } - - bool FindBitmap(int &ox, int &oy, long) { - ox = 1; - oy = 2; + ox = 1; + oy = 2; return true; } -TEST_CASE("Mismatched numeric types only convert necessary params") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Mismatched numeric types only convert necessary params") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&FindBitmap), "FindBitmap"); int x = 0; int y = 0; chai.add(chaiscript::var(&x), "x"); chai.add(chaiscript::var(&y), "y"); - chai.eval( "if ( FindBitmap ( x, y, 0) ) { print(\"found at \" + to_string(x) + \", \" + to_string(y))}" ); + chai.eval("if ( FindBitmap ( x, y, 0) ) { print(\"found at \" + to_string(x) + \", \" + to_string(y))}"); CHECK(x == 1); CHECK(y == 2); - } -TEST_CASE("type_conversion to bool") -{ +TEST_CASE("type_conversion to bool") { auto module = std::make_shared(); struct T { operator bool() const { return true; } @@ -1007,28 +906,22 @@ TEST_CASE("type_conversion to bool") module->add(chaiscript::type_conversion()); } -TEST_CASE("Make sure ChaiScript object still compiles / executes") -{ +TEST_CASE("Make sure ChaiScript object still compiles / executes") { chaiscript::ChaiScript chai; } -struct Count_Tracer -{ +struct Count_Tracer { int count = 0; template - void trace(const chaiscript::detail::Dispatch_State &, const chaiscript::eval::AST_Node_Impl *) - { - ++count; - } + void trace(const chaiscript::detail::Dispatch_State &, const chaiscript::eval::AST_Node_Impl *) { + ++count; + } }; +TEST_CASE("Test count tracer") { + using Parser_Type = chaiscript::parser::ChaiScript_Parser, chaiscript::optimizer::Optimizer_Default>; -TEST_CASE("Test count tracer") -{ - typedef chaiscript::parser::ChaiScript_Parser< chaiscript::eval::Tracer, chaiscript::optimizer::Optimizer_Default > Parser_Type; - - chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), - std::make_unique()); + chaiscript::ChaiScript_Basic chai(chaiscript::Std_Lib::library(), std::make_unique()); Parser_Type &parser = dynamic_cast(chai.get_parser()); @@ -1039,89 +932,94 @@ TEST_CASE("Test count tracer") CHECK(parser.get_tracer().count > count); } - -TEST_CASE("Test stdlib options") -{ - const auto test_has_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { +TEST_CASE("Test stdlib options") { + const auto test_has_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { CHECK_NOTHROW(chai.eval("`use`")); CHECK_NOTHROW(chai.eval("`eval_file`")); }; - const auto test_no_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { + const auto test_no_external_scripts = [](chaiscript::ChaiScript_Basic &chai) { CHECK_THROWS(chai.eval("`use`")); CHECK_THROWS(chai.eval("`eval_file`")); }; - const auto test_has_load_modules = [](chaiscript::ChaiScript_Basic &chai) { - CHECK_NOTHROW(chai.eval("`load_module`")); - }; + const auto test_has_load_modules = [](chaiscript::ChaiScript_Basic &chai) { CHECK_NOTHROW(chai.eval("`load_module`")); }; - const auto test_no_load_modules = [](chaiscript::ChaiScript_Basic &chai) { - CHECK_THROWS(chai.eval("`load_module`")); - }; + const auto test_no_load_modules = [](chaiscript::ChaiScript_Basic &chai) { CHECK_THROWS(chai.eval("`load_module`")); }; - SECTION( "Defaults" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + SECTION("Defaults") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); test_has_external_scripts(chai); test_has_load_modules(chai); } - SECTION( "Load_Modules, External_Scripts" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::Load_Modules, chaiscript::Options::External_Scripts} ); + SECTION("Load_Modules, External_Scripts") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::Load_Modules, chaiscript::Options::External_Scripts}); test_has_external_scripts(chai); test_has_load_modules(chai); } - SECTION( "No_Load_Modules, No_External_Scripts" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts} ); + SECTION("No_Load_Modules, No_External_Scripts") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::No_External_Scripts}); test_no_external_scripts(chai); test_no_load_modules(chai); } - SECTION( "No_Load_Modules, Load_Modules" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::No_Load_Modules, chaiscript::Options::Load_Modules} ); + SECTION("No_Load_Modules, Load_Modules") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_Load_Modules, chaiscript::Options::Load_Modules}); test_no_external_scripts(chai); test_no_load_modules(chai); } - SECTION( "No_External_Scripts, External_Scripts" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::No_External_Scripts, chaiscript::Options::External_Scripts} ); + SECTION("No_External_Scripts, External_Scripts") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_External_Scripts, chaiscript::Options::External_Scripts}); test_no_external_scripts(chai); test_no_load_modules(chai); } - SECTION( "No_External_Scripts, Load_Modules" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::No_External_Scripts, chaiscript::Options::Load_Modules} ); + SECTION("No_External_Scripts, Load_Modules") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::No_External_Scripts, chaiscript::Options::Load_Modules}); test_no_external_scripts(chai); test_has_load_modules(chai); } - SECTION( "External_Scripts, No_Load_Modules" ) { - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser(), {}, {}, - {chaiscript::Options::External_Scripts, chaiscript::Options::No_Load_Modules} ); + SECTION("External_Scripts, No_Load_Modules") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), + create_chaiscript_parser(), + {}, + {}, + {chaiscript::Options::External_Scripts, chaiscript::Options::No_Load_Modules}); test_has_external_scripts(chai); test_no_load_modules(chai); } } +void uservalueref(int &&) {} -void uservalueref(int &&) -{ -} +void usemoveonlytype(std::unique_ptr &&) {} -void usemoveonlytype(std::unique_ptr &&) -{ -} - - -TEST_CASE("Pass r-value reference to func") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Pass r-value reference to func") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::fun(&uservalueref), "uservalueref"); chai.add(chaiscript::fun(&usemoveonlytype), "usemoveonlytype"); @@ -1130,19 +1028,17 @@ TEST_CASE("Pass r-value reference to func") chai.eval("usemoveonlytype(iptr)"); } -TEST_CASE("Use unique_ptr") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Use unique_ptr") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); - chai.add(chaiscript::fun([](int &i){ ++i; }), "inci"); - chai.add(chaiscript::fun([](int i){ ++i; }), "copyi"); - chai.add(chaiscript::fun([](int *i){ ++(*i); }), "derefi"); - chai.add(chaiscript::fun([](const std::unique_ptr &i){ ++(*i); }), "constrefuniqptri"); - chai.add(chaiscript::fun([](std::unique_ptr &i){ ++(*i); }), "refuniqptri"); - chai.add(chaiscript::fun([](std::unique_ptr &&i){ ++(*i); }), "rvaluniqptri"); + chai.add(chaiscript::fun([](int &i) { ++i; }), "inci"); + chai.add(chaiscript::fun([](int i) { ++i; }), "copyi"); + chai.add(chaiscript::fun([](int *i) { ++(*i); }), "derefi"); + chai.add(chaiscript::fun([](const std::unique_ptr &i) { ++(*i); }), "constrefuniqptri"); + chai.add(chaiscript::fun([](std::unique_ptr &i) { ++(*i); }), "refuniqptri"); + chai.add(chaiscript::fun([](std::unique_ptr &&i) { ++(*i); }), "rvaluniqptri"); chai.add(chaiscript::var(std::make_unique(1)), "iptr"); - CHECK(chai.eval("iptr") == 1); chai.eval("inci(iptr)"); CHECK(chai.eval("iptr") == 2); @@ -1158,29 +1054,24 @@ TEST_CASE("Use unique_ptr") CHECK(chai.eval("iptr") == 6); } +class Unique_Ptr_Test_Class { +public: + Unique_Ptr_Test_Class() = default; + Unique_Ptr_Test_Class(const Unique_Ptr_Test_Class &) = default; + Unique_Ptr_Test_Class(Unique_Ptr_Test_Class &&) = default; + Unique_Ptr_Test_Class &operator=(const Unique_Ptr_Test_Class &) = default; + Unique_Ptr_Test_Class &operator=(Unique_Ptr_Test_Class &&) = default; + virtual ~Unique_Ptr_Test_Class() = default; -class Unique_Ptr_Test_Class -{ - public: - Unique_Ptr_Test_Class() = default; - Unique_Ptr_Test_Class(const Unique_Ptr_Test_Class&) = default; - Unique_Ptr_Test_Class(Unique_Ptr_Test_Class &&) = default; - Unique_Ptr_Test_Class &operator=(const Unique_Ptr_Test_Class&) = default; - Unique_Ptr_Test_Class &operator=(Unique_Ptr_Test_Class&&) = default; - virtual ~Unique_Ptr_Test_Class() = default; - - int getI() const {return 5;} + int getI() const { return 5; } }; - -std::unique_ptr make_Unique_Ptr_Test_Class() -{ +std::unique_ptr make_Unique_Ptr_Test_Class() { return std::make_unique(); } -TEST_CASE("Call methods through unique_ptr") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Call methods through unique_ptr") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::var(std::make_unique()), "uptr"); chai.add(chaiscript::fun(make_Unique_Ptr_Test_Class), "make_Unique_Ptr_Test_Class"); @@ -1189,24 +1080,20 @@ TEST_CASE("Call methods through unique_ptr") CHECK(chai.eval("var uptr2 = make_Unique_Ptr_Test_Class(); uptr2.getI()") == 5); } - -class Unique_Ptr_Test_Base_Class -{ - public: - int getI() const {return 5;} +class Unique_Ptr_Test_Base_Class { +public: + int getI() const { return 5; } }; -class Unique_Ptr_Test_Derived_Class : public Unique_Ptr_Test_Base_Class -{}; +class Unique_Ptr_Test_Derived_Class : public Unique_Ptr_Test_Base_Class { +}; -std::unique_ptr make_Unique_Ptr_Test_Derived_Class() -{ +std::unique_ptr make_Unique_Ptr_Test_Derived_Class() { return std::make_unique(); } -TEST_CASE("Call methods on base class through unique_ptr") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Call methods on base class through unique_ptr") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::var(std::make_unique()), "uptr"); chai.add(chaiscript::fun(make_Unique_Ptr_Test_Derived_Class), "make_Unique_Ptr_Test_Derived_Class"); @@ -1216,27 +1103,23 @@ TEST_CASE("Call methods on base class through unique_ptr") CHECK(chai.eval("var uptr2 = make_Unique_Ptr_Test_Derived_Class(); uptr2.getI()") == 5); } - -class A -{ - public: - A() = default; - A(const A&) = default; - A(A &&) = default; - A &operator=(const A&) = default; - A &operator=(A&&) = default; - virtual ~A() = default; +class A { +public: + A() = default; + A(const A &) = default; + A(A &&) = default; + A &operator=(const A &) = default; + A &operator=(A &&) = default; + virtual ~A() = default; }; -class B : public A -{ - public: - B() = default; +class B : public A { +public: + B() = default; }; -TEST_CASE("Test typed chaiscript functions to perform conversions") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test typed chaiscript functions to perform conversions") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); //------------------------------------------------------------------------- @@ -1245,20 +1128,15 @@ TEST_CASE("Test typed chaiscript functions to perform conversions") chai.add(chaiscript::user_type(), "B"); chai.add(chaiscript::base_class()); - chai.add(chaiscript::fun([](const B &) - { - }), "CppFunctWithBArg"); + chai.add(chaiscript::fun([](const B &) {}), "CppFunctWithBArg"); - chai.add(chaiscript::fun([]() -> std::shared_ptr - { - return (std::shared_ptr(new B())); - }), "Create"); + chai.add(chaiscript::fun([]() -> std::shared_ptr { return (std::shared_ptr(new B())); }), "Create"); chai.eval(R"( var inst = Create() // A* // it prints "A" - inst.type_name().print() + inst.type_name().print() // Ok it is casted using conversion CppFunctWithBArg(inst) @@ -1274,15 +1152,15 @@ TEST_CASE("Test typed chaiscript functions to perform conversions") )"); } -struct Reference_MyClass -{ - Reference_MyClass(double& t_x) : x(t_x) {} - double& x; +struct Reference_MyClass { + Reference_MyClass(double &t_x) + : x(t_x) { + } + double &x; }; -TEST_CASE("Test reference member being registered") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test reference member being registered") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); // Note, C++ will not allow us to do this: // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x"); chai.add(chaiscript::fun([](Reference_MyClass &r) -> decltype(auto) { return (r.x); }), "x"); @@ -1293,25 +1171,22 @@ TEST_CASE("Test reference member being registered") CHECK(d == Approx(2.3)); } -TEST_CASE("Test unicode matches C++") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); - CHECK(u8"\U000000AC" == chai.eval(R"("\U000000AC")")); +// starting with C++20 u8"" strings cannot be compared with std::string +// and the support for std::u8strings is still terrible. +TEST_CASE("Test unicode matches C++") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); + CHECK("\U000000AC" == chai.eval(R"("\U000000AC")")); CHECK("\xF0\x9F\x8D\x8C" == chai.eval(R"("\xF0\x9F\x8D\x8C")")); - CHECK(u8"\U0001F34C" == chai.eval(R"("\U0001F34C")")); - CHECK(u8"\u2022" == chai.eval(R"("\u2022")")); - + CHECK("\U0001F34C" == chai.eval(R"("\U0001F34C")")); + CHECK("\u2022" == chai.eval(R"("\u2022")")); } - -const int add_3(const int &i) -{ +const int add_3(const int &i) { return i + 3; } -TEST_CASE("Test returning by const non-reference") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test returning by const non-reference") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); // Note, C++ will not allow us to do this: // chai.add(chaiscript::fun(&Reference_MyClass::x) , "x"); chai.add(chaiscript::fun(&add_3), "add_3"); @@ -1319,21 +1194,17 @@ TEST_CASE("Test returning by const non-reference") CHECK(v == 15); } - -struct MyException : std::runtime_error -{ +struct MyException : std::runtime_error { using std::runtime_error::runtime_error; int value = 5; }; -void throws_a_thing() -{ +void throws_a_thing() { throw MyException("Hello World"); } -TEST_CASE("Test throwing and catching custom exception") -{ - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); +TEST_CASE("Test throwing and catching custom exception") { + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); chai.add(chaiscript::user_type(), "MyException"); chai.add(chaiscript::base_class()); // be sure to register base class relationship chai.add(chaiscript::fun(&throws_a_thing), "throws_a_thing"); @@ -1343,15 +1214,72 @@ TEST_CASE("Test throwing and catching custom exception") CHECK(s == "Hello World"); // this has an explicit clone to prevent returning a pointer to the `value` from inside of MyException - const auto i = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()"); + const auto i + = chai.eval("fun(){ try { throws_a_thing(); } catch (MyException ex) { var v = clone(ex.value); print(v); return v; } }()"); CHECK(i == 5); } - -TEST_CASE("Test ability to get 'use' function from default construction") -{ +TEST_CASE("Test ability to get 'use' function from default construction") { chaiscript::ChaiScript chai; - const auto use_function = chai.eval>("use"); + const auto use_function = chai.eval>("use"); } +TEST_CASE("Throw an exception when trying to add same conversion twice") { + struct my_int { + int value; + my_int(int val) + : value(val) { + } + }; + chaiscript::ChaiScript chai; + chai.add(chaiscript::type_conversion([](int x) { + std::cout << "My_int type conversion 1\n"; + return my_int(x); + })); + CHECK_THROWS_AS(chai.add(chaiscript::type_conversion([](int x) { + std::cout << "My_int type conversion 2\n"; + return my_int(x); + })), + chaiscript::exception::conversion_error); +} + +TEST_CASE("Test if non copyable/movable types can be registered") { + struct Noncopyable { + Noncopyable() { str = "test"; } + Noncopyable(const Noncopyable &) = delete; + Noncopyable &operator=(const Noncopyable &) = delete; + + std::string str; + }; + + struct Nonmovable { + Nonmovable() { str = "test"; } + Nonmovable(Nonmovable &&) = delete; + Nonmovable &operator=(Nonmovable &&) = delete; + + std::string str; + }; + + struct Nothing { + Nothing() { str = "test"; } + + Nothing(Nothing &&) = delete; + Nothing &operator=(Nothing &&) = delete; + + Nothing(const Nothing &) = delete; + Nothing &operator=(const Nothing &) = delete; + + std::string str; + }; + + chaiscript::ChaiScript chai; + chai.add(chaiscript::user_type(), "Noncopyable"); + chai.add(chaiscript::constructor(), "Noncopyable"); + + chai.add(chaiscript::user_type(), "Nonmovable"); + chai.add(chaiscript::constructor(), "Nonmovable"); + + chai.add(chaiscript::user_type(), "Nothing"); + chai.add(chaiscript::constructor(), "Nothing"); +} diff --git a/unittests/exception_guards.chai b/unittests/exception_guards.chai deleted file mode 100644 index 12792985..00000000 --- a/unittests/exception_guards.chai +++ /dev/null @@ -1,34 +0,0 @@ -auto results = []; - -for (auto i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results); diff --git a/unittests/exception_typed_2.chai b/unittests/exception_typed_2.chai deleted file mode 100644 index cb294c78..00000000 --- a/unittests/exception_typed_2.chai +++ /dev/null @@ -1,34 +0,0 @@ -auto results = []; - -for (auto i = 2; i < 6; ++i) { - try { - throw(i) - } - catch(int e) : e < 2 { - results.push_back("c1: " + e.to_string()); - } - catch(int e) : e < 4 { - results.push_back("c2: " + e.to_string()); - } - catch(e) { - results.push_back("c3: " + e.to_string()); - } - catch { - // Should never get called - assert_equal(false, true) - } -} - -try { - throw(3) -} -catch(int e) : e < 3 -{ - // Should never get called - assert_equal(false, true); -} -catch { - results.push_back("defaultcatch"); -} - -assert_equal(["c2: 2", "c2: 3", "c3: 4", "c3: 5", "defaultcatch"], results); diff --git a/unittests/instring_eval_more.chai b/unittests/instring_eval_more.chai index 17768f82..585865ed 100644 --- a/unittests/instring_eval_more.chai +++ b/unittests/instring_eval_more.chai @@ -2,3 +2,7 @@ assert_equal("\$ {4 + 5}", "$ {4 + 5}") assert_equal("\$9", "$${4+5}") assert_equal("Value: \${4 + 5}", "Value: \${4 + 5}") assert_equal("Value: \$9", "Value: \$${4 + 5}") +assert_equal("\$code\$", "$code$") +assert_equal("\$\$", "$$") +assert_equal("\$", "$") +assert_equal("\${", "\${") // ensure this doesn't fail diff --git a/unittests/integer_literal_test.cpp b/unittests/integer_literal_test.cpp index 3187b65d..147ca1dc 100644 --- a/unittests/integer_literal_test.cpp +++ b/unittests/integer_literal_test.cpp @@ -1,7 +1,6 @@ -#include #include "../static_libs/chaiscript_parser.hpp" #include "../static_libs/chaiscript_stdlib.hpp" - +#include #ifdef CHAISCRIPT_MSVC // ignore errors about negative unsigned integer literals @@ -9,104 +8,51 @@ #pragma warning(disable : 4146) #endif - #define TEST_LITERAL(v) test_literal(v, #v) #define TEST_LITERAL_SIGNED(v) test_literal(v, #v, true) template -bool test_literal(T val, const std::string &str, bool use_boxed_number = false) -{ +bool test_literal(T val, const std::string &str, bool use_boxed_number = false) { std::cout << '(' << str << ") Comparing : C++ '" << val; - chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(),create_chaiscript_parser()); + chaiscript::ChaiScript_Basic chai(create_chaiscript_stdlib(), create_chaiscript_parser()); // Note, after applying the `-` it's possible that chaiscript has internally converted // between two equivalently sized types (ie, unsigned long and unsigned long long on a 64bit system) // so we're going to allow some leeway with the signed tests - T val2 = [&](){ + T val2 = [&]() { if (!use_boxed_number) { return chai.eval(str); - } else { - return chai.eval(str).get_as_checked(); } + return chai.eval(str).get_as_checked(); }(); std::cout << "' chai '" << val2 << "'\n"; return val == val2; } -int main() -{ - if( - TEST_LITERAL(0xF) - && TEST_LITERAL(0xFF) - && TEST_LITERAL(0xFFF) - && TEST_LITERAL(0xFFFF) - && TEST_LITERAL(0xFFFFF) - && TEST_LITERAL(0xFFFFFF) - && TEST_LITERAL(0xFFFFFFF) - && TEST_LITERAL(0xFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFFFFFF) - && TEST_LITERAL(0xFFFFFFFFFFFFFFFF) +int main() { + if (TEST_LITERAL(0xF) && TEST_LITERAL(0xFF) && TEST_LITERAL(0xFFF) && TEST_LITERAL(0xFFFF) && TEST_LITERAL(0xFFFFF) + && TEST_LITERAL(0xFFFFFF) && TEST_LITERAL(0xFFFFFFF) && TEST_LITERAL(0xFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFF) + && TEST_LITERAL(0xFFFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFFFFFF) + && TEST_LITERAL(0xFFFFFFFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFFFFFFFF) && TEST_LITERAL(0xFFFFFFFFFFFFFFFF) + && TEST_LITERAL(01) && TEST_LITERAL(017) && TEST_LITERAL(0177) && TEST_LITERAL(01777) && TEST_LITERAL(017777) + && TEST_LITERAL(0177777) && TEST_LITERAL(01777777) && TEST_LITERAL(017777777) && TEST_LITERAL(0177777777) + && TEST_LITERAL(01777777777) && TEST_LITERAL(017777777777) && TEST_LITERAL(0177777777777) && TEST_LITERAL(01777777777777) + && TEST_LITERAL(017777777777777) && TEST_LITERAL(0177777777777777) && TEST_LITERAL(01777777777777777) + && TEST_LITERAL(017777777777777777) && TEST_LITERAL(0177777777777777777) && TEST_LITERAL(01777777777777777777) + && TEST_LITERAL(017777777777777777777) && TEST_LITERAL(0177777777777777777777) && TEST_LITERAL(01777777777777777777777) - && TEST_LITERAL(01) - && TEST_LITERAL(017) - && TEST_LITERAL(0177) - && TEST_LITERAL(01777) - && TEST_LITERAL(017777) - && TEST_LITERAL(0177777) - && TEST_LITERAL(01777777) - && TEST_LITERAL(017777777) - && TEST_LITERAL(0177777777) - && TEST_LITERAL(01777777777) - && TEST_LITERAL(017777777777) - && TEST_LITERAL(0177777777777) - && TEST_LITERAL(01777777777777) - && TEST_LITERAL(017777777777777) - && TEST_LITERAL(0177777777777777) - && TEST_LITERAL(01777777777777777) - && TEST_LITERAL(017777777777777777) - && TEST_LITERAL(0177777777777777777) - && TEST_LITERAL(01777777777777777777) - && TEST_LITERAL(017777777777777777777) - && TEST_LITERAL(0177777777777777777777) - && TEST_LITERAL(01777777777777777777777) + && TEST_LITERAL(1) && TEST_LITERAL(17) && TEST_LITERAL(177) && TEST_LITERAL(1777) && TEST_LITERAL(17777) && TEST_LITERAL(177777) + && TEST_LITERAL(1777777) && TEST_LITERAL(17777777) && TEST_LITERAL(177777777) && TEST_LITERAL(1777777777) + && TEST_LITERAL(17777777777) && TEST_LITERAL(177777777777) && TEST_LITERAL(1777777777777) && TEST_LITERAL(17777777777777) + && TEST_LITERAL(177777777777777) && TEST_LITERAL(1777777777777777) && TEST_LITERAL(17777777777777777) + && TEST_LITERAL(177777777777777777) && TEST_LITERAL(1777777777777777777) - && TEST_LITERAL(1) - && TEST_LITERAL(17) - && TEST_LITERAL(177) - && TEST_LITERAL(1777) - && TEST_LITERAL(17777) - && TEST_LITERAL(177777) - && TEST_LITERAL(1777777) - && TEST_LITERAL(17777777) - && TEST_LITERAL(177777777) - && TEST_LITERAL(1777777777) - && TEST_LITERAL(17777777777) - && TEST_LITERAL(177777777777) - && TEST_LITERAL(1777777777777) - && TEST_LITERAL(17777777777777) - && TEST_LITERAL(177777777777777) - && TEST_LITERAL(1777777777777777) - && TEST_LITERAL(17777777777777777) - && TEST_LITERAL(177777777777777777) - && TEST_LITERAL(1777777777777777777) - - && test_literal(0xF, "0b1111") - && test_literal(0xFF, "0b11111111") - && test_literal(0xFFF, "0b111111111111") - && test_literal(0xFFFF, "0b1111111111111111") - && test_literal(0xFFFFF, "0b11111111111111111111") - && test_literal(0xFFFFFF, "0b111111111111111111111111") - && test_literal(0xFFFFFFF, "0b1111111111111111111111111111") - && test_literal(0xFFFFFFFF, "0b11111111111111111111111111111111") - && test_literal(0xFFFFFFFFF, "0b111111111111111111111111111111111111") + && test_literal(0xF, "0b1111") && test_literal(0xFF, "0b11111111") && test_literal(0xFFF, "0b111111111111") + && test_literal(0xFFFF, "0b1111111111111111") && test_literal(0xFFFFF, "0b11111111111111111111") + && test_literal(0xFFFFFF, "0b111111111111111111111111") && test_literal(0xFFFFFFF, "0b1111111111111111111111111111") + && test_literal(0xFFFFFFFF, "0b11111111111111111111111111111111") && test_literal(0xFFFFFFFFF, "0b111111111111111111111111111111111111") && test_literal(0xFFFFFFFFFF, "0b1111111111111111111111111111111111111111") && test_literal(0xFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111") && test_literal(0xFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111") @@ -115,15 +61,10 @@ int main() && test_literal(0xFFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111") && test_literal(0xFFFFFFFFFFFFFFFF, "0b1111111111111111111111111111111111111111111111111111111111111111") - && test_literal(0x7, "0b111") - && test_literal(0x7F, "0b1111111") - && test_literal(0x7FF, "0b11111111111") - && test_literal(0x7FFF, "0b111111111111111") - && test_literal(0x7FFFF, "0b1111111111111111111") - && test_literal(0x7FFFFF, "0b11111111111111111111111") - && test_literal(0x7FFFFFF, "0b111111111111111111111111111") - && test_literal(0x7FFFFFFF, "0b1111111111111111111111111111111") - && test_literal(0x7FFFFFFFF, "0b11111111111111111111111111111111111") + && test_literal(0x7, "0b111") && test_literal(0x7F, "0b1111111") && test_literal(0x7FF, "0b11111111111") + && test_literal(0x7FFF, "0b111111111111111") && test_literal(0x7FFFF, "0b1111111111111111111") + && test_literal(0x7FFFFF, "0b11111111111111111111111") && test_literal(0x7FFFFFF, "0b111111111111111111111111111") + && test_literal(0x7FFFFFFF, "0b1111111111111111111111111111111") && test_literal(0x7FFFFFFFF, "0b11111111111111111111111111111111111") && test_literal(0x7FFFFFFFFF, "0b111111111111111111111111111111111111111") && test_literal(0x7FFFFFFFFFF, "0b1111111111111111111111111111111111111111111") && test_literal(0x7FFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111") @@ -132,153 +73,82 @@ int main() && test_literal(0x7FFFFFFFFFFFFFF, "0b11111111111111111111111111111111111111111111111111111111111") && test_literal(0x7FFFFFFFFFFFFFFF, "0b111111111111111111111111111111111111111111111111111111111111111") + && TEST_LITERAL_SIGNED(-0xF) && TEST_LITERAL_SIGNED(-0xFF) && TEST_LITERAL_SIGNED(-0xFFF) && TEST_LITERAL_SIGNED(-0xFFFF) + && TEST_LITERAL_SIGNED(-0xFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFF) + && TEST_LITERAL_SIGNED(-0xFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFFF) + && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFF) + && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFFF) && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xF) - && TEST_LITERAL_SIGNED(-0xFF) - && TEST_LITERAL_SIGNED(-0xFFF) - && TEST_LITERAL_SIGNED(-0xFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFFF) - && TEST_LITERAL_SIGNED(-0xFFFFFFFFFFFFFFFF) + && TEST_LITERAL_SIGNED(-01) && TEST_LITERAL_SIGNED(-017) && TEST_LITERAL_SIGNED(-0177) && TEST_LITERAL_SIGNED(-01777) + && TEST_LITERAL_SIGNED(-017777) && TEST_LITERAL_SIGNED(-0177777) && TEST_LITERAL_SIGNED(-01777777) && TEST_LITERAL_SIGNED(-017777777) + && TEST_LITERAL_SIGNED(-0177777777) && TEST_LITERAL_SIGNED(-01777777777) && TEST_LITERAL_SIGNED(-017777777777) + && TEST_LITERAL_SIGNED(-0177777777777) && TEST_LITERAL_SIGNED(-01777777777777) && TEST_LITERAL_SIGNED(-017777777777777) + && TEST_LITERAL_SIGNED(-0177777777777777) && TEST_LITERAL_SIGNED(-01777777777777777) && TEST_LITERAL_SIGNED(-017777777777777777) + && TEST_LITERAL_SIGNED(-0177777777777777777) && TEST_LITERAL_SIGNED(-01777777777777777777) && TEST_LITERAL_SIGNED(-017777777777777777777) + && TEST_LITERAL_SIGNED(-0177777777777777777777) && TEST_LITERAL_SIGNED(-01777777777777777777777) - - && TEST_LITERAL_SIGNED(-01) - && TEST_LITERAL_SIGNED(-017) - && TEST_LITERAL_SIGNED(-0177) - && TEST_LITERAL_SIGNED(-01777) - && TEST_LITERAL_SIGNED(-017777) - && TEST_LITERAL_SIGNED(-0177777) - && TEST_LITERAL_SIGNED(-01777777) - && TEST_LITERAL_SIGNED(-017777777) - && TEST_LITERAL_SIGNED(-0177777777) - && TEST_LITERAL_SIGNED(-01777777777) - && TEST_LITERAL_SIGNED(-017777777777) - && TEST_LITERAL_SIGNED(-0177777777777) - && TEST_LITERAL_SIGNED(-01777777777777) - && TEST_LITERAL_SIGNED(-017777777777777) - && TEST_LITERAL_SIGNED(-0177777777777777) - && TEST_LITERAL_SIGNED(-01777777777777777) - && TEST_LITERAL_SIGNED(-017777777777777777) - && TEST_LITERAL_SIGNED(-0177777777777777777) - && TEST_LITERAL_SIGNED(-01777777777777777777) - && TEST_LITERAL_SIGNED(-017777777777777777777) - && TEST_LITERAL_SIGNED(-0177777777777777777777) - && TEST_LITERAL_SIGNED(-01777777777777777777777) - - && TEST_LITERAL_SIGNED(-1) - && TEST_LITERAL_SIGNED(-17) - && TEST_LITERAL_SIGNED(-177) - && TEST_LITERAL_SIGNED(-1777) - && TEST_LITERAL_SIGNED(-17777) - && TEST_LITERAL_SIGNED(-177777) - && TEST_LITERAL_SIGNED(-1777777) - && TEST_LITERAL_SIGNED(-17777777) - && TEST_LITERAL_SIGNED(-177777777) - && TEST_LITERAL_SIGNED(-1777777777) - && TEST_LITERAL_SIGNED(-17777777777) - && TEST_LITERAL_SIGNED(-177777777777) - && TEST_LITERAL_SIGNED(-1777777777777) - && TEST_LITERAL_SIGNED(-17777777777777) - && TEST_LITERAL_SIGNED(-177777777777777) - && TEST_LITERAL_SIGNED(-1777777777777777) - && TEST_LITERAL_SIGNED(-17777777777777777) + && TEST_LITERAL_SIGNED(-1) && TEST_LITERAL_SIGNED(-17) && TEST_LITERAL_SIGNED(-177) && TEST_LITERAL_SIGNED(-1777) + && TEST_LITERAL_SIGNED(-17777) && TEST_LITERAL_SIGNED(-177777) && TEST_LITERAL_SIGNED(-1777777) && TEST_LITERAL_SIGNED(-17777777) + && TEST_LITERAL_SIGNED(-177777777) && TEST_LITERAL_SIGNED(-1777777777) && TEST_LITERAL_SIGNED(-17777777777) + && TEST_LITERAL_SIGNED(-177777777777) && TEST_LITERAL_SIGNED(-1777777777777) && TEST_LITERAL_SIGNED(-17777777777777) + && TEST_LITERAL_SIGNED(-177777777777777) && TEST_LITERAL_SIGNED(-1777777777777777) && TEST_LITERAL_SIGNED(-17777777777777777) && TEST_LITERAL_SIGNED(-177777777777777777) && TEST_LITERAL_SIGNED(-1777777777777777777) // Test 8/16/24/32 bit boundaries for various types - && TEST_LITERAL(255) - && TEST_LITERAL(65535) + && TEST_LITERAL(255) && TEST_LITERAL(65535) && TEST_LITERAL(16777215) #ifndef CHAISCRIPT_MSVC - // bug in cl.exe causes this to be incorrectly parsed as an unsigned - && TEST_LITERAL(4294967295) + // bug in cl.exe causes this to be incorrectly parsed as an unsigned + && TEST_LITERAL(4294967295) #endif - && TEST_LITERAL_SIGNED(-255) - && TEST_LITERAL_SIGNED(-65535) + && TEST_LITERAL_SIGNED(-255) && TEST_LITERAL_SIGNED(-65535) && TEST_LITERAL_SIGNED(-16777215) #ifndef CHAISCRIPT_MSVC - // bug in cl.exe causes this to be incorrectly parsed as an unsigned - && TEST_LITERAL_SIGNED(-4294967295) + // bug in cl.exe causes this to be incorrectly parsed as an unsigned + && TEST_LITERAL_SIGNED(-4294967295) #endif - && TEST_LITERAL(255u) - && TEST_LITERAL(65535u) - && TEST_LITERAL(16777215u) - && TEST_LITERAL(4294967295u) + && TEST_LITERAL(255u) && TEST_LITERAL(65535u) && TEST_LITERAL(16777215u) && TEST_LITERAL(4294967295u) - && TEST_LITERAL_SIGNED(-255u) - && TEST_LITERAL_SIGNED(-65535u) - && TEST_LITERAL_SIGNED(-16777215u) - && TEST_LITERAL_SIGNED(-4294967295u) + && TEST_LITERAL_SIGNED(-255u) && TEST_LITERAL_SIGNED(-65535u) && TEST_LITERAL_SIGNED(-16777215u) && TEST_LITERAL_SIGNED(-4294967295u) - && TEST_LITERAL(255l) - && TEST_LITERAL(65535l) + && TEST_LITERAL(255l) && TEST_LITERAL(65535l) && TEST_LITERAL(16777215l) #ifndef CHAISCRIPT_MSVC - // bug in cl.exe causes this to be incorrectly parsed as an unsigned - && TEST_LITERAL(4294967295l) + // bug in cl.exe causes this to be incorrectly parsed as an unsigned + && TEST_LITERAL(4294967295l) #endif - && TEST_LITERAL_SIGNED(-255l) - && TEST_LITERAL_SIGNED(-65535l) + && TEST_LITERAL_SIGNED(-255l) && TEST_LITERAL_SIGNED(-65535l) && TEST_LITERAL_SIGNED(-16777215l) #ifndef CHAISCRIPT_MSVC - // bug in cl.exe causes this to be incorrectly parsed as an unsigned - && TEST_LITERAL_SIGNED(-4294967295l) + // bug in cl.exe causes this to be incorrectly parsed as an unsigned + && TEST_LITERAL_SIGNED(-4294967295l) #endif - && TEST_LITERAL(255ul) - && TEST_LITERAL(65535ul) - && TEST_LITERAL(16777215ul) - && TEST_LITERAL(4294967295ul) + && TEST_LITERAL(255ul) && TEST_LITERAL(65535ul) && TEST_LITERAL(16777215ul) && TEST_LITERAL(4294967295ul) - && TEST_LITERAL_SIGNED(-255ul) - && TEST_LITERAL_SIGNED(-65535ul) - && TEST_LITERAL_SIGNED(-16777215ul) - && TEST_LITERAL_SIGNED(-4294967295ul) + && TEST_LITERAL_SIGNED(-255ul) && TEST_LITERAL_SIGNED(-65535ul) && TEST_LITERAL_SIGNED(-16777215ul) && TEST_LITERAL_SIGNED(-4294967295ul) - && TEST_LITERAL(255ull) - && TEST_LITERAL(65535ull) - && TEST_LITERAL(16777215ull) - && TEST_LITERAL(4294967295ull) + && TEST_LITERAL(255ull) && TEST_LITERAL(65535ull) && TEST_LITERAL(16777215ull) && TEST_LITERAL(4294967295ull) - && TEST_LITERAL_SIGNED(-255ull) - && TEST_LITERAL_SIGNED(-65535ull) - && TEST_LITERAL_SIGNED(-16777215ull) + && TEST_LITERAL_SIGNED(-255ull) && TEST_LITERAL_SIGNED(-65535ull) && TEST_LITERAL_SIGNED(-16777215ull) && TEST_LITERAL_SIGNED(-4294967295ull) - && TEST_LITERAL(255ll) - && TEST_LITERAL(65535ll) - && TEST_LITERAL(16777215ll) - && TEST_LITERAL(4294967295ll) + && TEST_LITERAL(255ll) && TEST_LITERAL(65535ll) && TEST_LITERAL(16777215ll) && TEST_LITERAL(4294967295ll) - && TEST_LITERAL_SIGNED(-255ll) - && TEST_LITERAL_SIGNED(-65535ll) - && TEST_LITERAL_SIGNED(-16777215ll) - && TEST_LITERAL_SIGNED(-4294967295ll) + && TEST_LITERAL_SIGNED(-255ll) && TEST_LITERAL_SIGNED(-65535ll) && TEST_LITERAL_SIGNED(-16777215ll) && TEST_LITERAL_SIGNED(-4294967295ll) - - ) - { + ) { return EXIT_SUCCESS; - } else { - return EXIT_FAILURE; } + return EXIT_FAILURE; } - #ifdef CHAISCRIPT_MSVC #pragma warning(pop) #endif diff --git a/unittests/multifile_test_chai.cpp b/unittests/multifile_test_chai.cpp index f57157d4..f0a19fff 100644 --- a/unittests/multifile_test_chai.cpp +++ b/unittests/multifile_test_chai.cpp @@ -4,13 +4,11 @@ #include Multi_Test_Chai::Multi_Test_Chai() - : m_chai(new chaiscript::ChaiScript_Basic(chaiscript::Std_Lib::library(), - std::make_unique>())) -{ + : m_chai(new chaiscript::ChaiScript_Basic( + chaiscript::Std_Lib::library(), + std::make_unique>())) { } - -std::shared_ptr Multi_Test_Chai::get_chai() -{ +std::shared_ptr Multi_Test_Chai::get_chai() { return m_chai; } diff --git a/unittests/multifile_test_chai.hpp b/unittests/multifile_test_chai.hpp index 681c2c2a..7d8cb5ce 100644 --- a/unittests/multifile_test_chai.hpp +++ b/unittests/multifile_test_chai.hpp @@ -1,14 +1,11 @@ #include -class Multi_Test_Chai -{ - public: - Multi_Test_Chai(); +class Multi_Test_Chai { +public: + Multi_Test_Chai(); - std::shared_ptr get_chai(); + std::shared_ptr get_chai(); - private: - std::shared_ptr m_chai; +private: + std::shared_ptr m_chai; }; - - diff --git a/unittests/multifile_test_main.cpp b/unittests/multifile_test_main.cpp index 24b03b2d..1e3b7d83 100644 --- a/unittests/multifile_test_main.cpp +++ b/unittests/multifile_test_main.cpp @@ -3,8 +3,7 @@ #include -int main() -{ +int main() { Multi_Test_Chai chaitest; Multi_Test_Module chaimodule; diff --git a/unittests/multifile_test_module.cpp b/unittests/multifile_test_module.cpp index ec1df238..20ea3065 100644 --- a/unittests/multifile_test_module.cpp +++ b/unittests/multifile_test_module.cpp @@ -2,17 +2,13 @@ #include "multifile_test_module.hpp" -Multi_Test_Module::Multi_Test_Module() -{ -} +Multi_Test_Module::Multi_Test_Module() noexcept = default; -int Multi_Test_Module::get_module_value() -{ +int Multi_Test_Module::get_module_value() { return 0; } -chaiscript::ModulePtr Multi_Test_Module::get_module() -{ +chaiscript::ModulePtr Multi_Test_Module::get_module() { chaiscript::ModulePtr module(new chaiscript::Module()); module->add(chaiscript::fun(&Multi_Test_Module::get_module_value), "get_module_value"); diff --git a/unittests/multifile_test_module.hpp b/unittests/multifile_test_module.hpp index b09dca39..87427082 100644 --- a/unittests/multifile_test_module.hpp +++ b/unittests/multifile_test_module.hpp @@ -1,11 +1,10 @@ #include -class Multi_Test_Module -{ - public: - static int get_module_value(); +class Multi_Test_Module { +public: + static int get_module_value(); - Multi_Test_Module(); + Multi_Test_Module() noexcept; - chaiscript::ModulePtr get_module(); + chaiscript::ModulePtr get_module(); }; diff --git a/unittests/multiline.chai b/unittests/multiline.chai index dfa213c3..f4cd9d9c 100644 --- a/unittests/multiline.chai +++ b/unittests/multiline.chai @@ -1,3 +1,14 @@ +class MyClass { + var value; + def MyClass(v) { this.value = v; } + def getWrappedIncrement() { return MyClass(this.value + 1); } + def getValue { return this.value; } +}; + +def foo(x) { + return MyClass(x+1); +} + auto x = [1, 2, 3, 4] @@ -7,3 +18,21 @@ auto y = map(x, fun(x) { x + 1 }) assert_equal(2, y[0]) + +auto z = foo(1) +.value + +assert_equal(2, z) + +auto v = foo(2) +.getValue() + +assert_equal(3, v) + +auto u = MyClass(3) +.getWrappedIncrement() + +.getWrappedIncrement() +.getValue() + +assert_equal(5, u) \ No newline at end of file diff --git a/unittests/multithreaded_test.cpp b/unittests/multithreaded_test.cpp index e2d0240b..a59192d5 100644 --- a/unittests/multithreaded_test.cpp +++ b/unittests/multithreaded_test.cpp @@ -8,20 +8,17 @@ #include #include -int expected_value(int num_iters) -{ +int expected_value(int num_iters) { int i = 0; - for (int k = 0; k usepaths; - usepaths.push_back(""); - if (usepath) - { - usepaths.push_back(usepath); + usepaths.emplace_back(""); + if (usepath) { + usepaths.emplace_back(usepath); } std::vector modulepaths; #ifdef CHAISCRIPT_NO_DYNLOAD - chaiscript::ChaiScript chai(/* unused */modulepaths, usepaths); + chaiscript::ChaiScript chai(/* unused */ modulepaths, usepaths); #else - modulepaths.push_back(""); - if (modulepath) - { - modulepaths.push_back(modulepath); + modulepaths.emplace_back(""); + if (modulepath) { + modulepaths.emplace_back(modulepath); } - + // For this test we are going to load the dynamic stdlib // to make sure it continues to work chaiscript::ChaiScript_Basic chai( std::make_unique>(), - modulepaths,usepaths); + modulepaths, + usepaths); #endif - std::vector > threads; + std::vector> threads; // Ensure at least two, but say only 7 on an 8 core processor size_t num_threads = static_cast(std::max(static_cast(std::thread::hardware_concurrency()) - 1, 2)); std::cout << "Num threads: " << num_threads << '\n'; - for (size_t i = 0; i < num_threads; ++i) - { + for (size_t i = 0; i < num_threads; ++i) { threads.push_back(std::make_shared(do_work, std::ref(chai), i)); } - for (size_t i = 0; i < num_threads; ++i) - { + for (size_t i = 0; i < num_threads; ++i) { threads[i]->join(); } - - - for (size_t i = 0; i < num_threads; ++i) - { + for (size_t i = 0; i < num_threads; ++i) { std::stringstream ss; ss << i; - if (chai.eval("getvalue(" + ss.str() + ")") != expected_value(4000)) - { + if (chai.eval("getvalue(" + ss.str() + ")") != expected_value(4000)) { return EXIT_FAILURE; } - if (chai.eval("getid(" + ss.str() + ")") != static_cast(i)) - { + if (chai.eval("getid(" + ss.str() + ")") != static_cast(i)) { return EXIT_FAILURE; } } return EXIT_SUCCESS; } - diff --git a/unittests/type_info_test.cpp b/unittests/type_info_test.cpp index 57f02f33..df984a23 100644 --- a/unittests/type_info_test.cpp +++ b/unittests/type_info_test.cpp @@ -11,34 +11,27 @@ #pragma GCC diagnostic ignored "-Wparentheses" #endif - #include #include -#include #include - - - +#include #define CATCH_CONFIG_MAIN #include "catch.hpp" +TEST_CASE("Type_Info objects generate expected results") { + const auto test_type + = [](const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void, bool t_is_undef, bool t_is_arithmetic) { + CHECK(ti.is_const() == t_is_const); + CHECK(ti.is_pointer() == t_is_pointer); + CHECK(ti.is_reference() == t_is_reference); + CHECK(ti.is_void() == t_is_void); + CHECK(ti.is_undef() == t_is_undef); + CHECK(ti.is_arithmetic() == t_is_arithmetic); + }; -TEST_CASE("Type_Info objects generate expected results") -{ - const auto test_type = [](const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void, - bool t_is_undef, bool t_is_arithmetic) - { - CHECK(ti.is_const() == t_is_const); - CHECK(ti.is_pointer() == t_is_pointer); - CHECK(ti.is_reference() == t_is_reference); - CHECK(ti.is_void() == t_is_void); - CHECK(ti.is_undef() == t_is_undef); - CHECK(ti.is_arithmetic() == t_is_arithmetic); - }; - - SECTION("void") { test_type(chaiscript::user_type(), false, false, false, true, false, false); } + SECTION("void") { test_type(chaiscript::user_type(), false, false, false, true, false, false); } SECTION("const int") { test_type(chaiscript::user_type(), true, false, false, false, false, true); } SECTION("const int &") { test_type(chaiscript::user_type(), true, false, true, false, false, true); } SECTION("int") { test_type(chaiscript::user_type(), false, false, false, false, false, true); } @@ -49,6 +42,3 @@ TEST_CASE("Type_Info objects generate expected results") std::cout << "Size of Type_Info " << sizeof(chaiscript::Type_Info) << '\n'; } - - -