diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 04e8819e..36b0b638 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -83,4 +83,4 @@ jobs: - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 769ad56e..791403d8 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -26,21 +26,22 @@ jobs: fail-fast: false max-parallel: 8 matrix: - compiler: [g++-9, g++-10, g++-11, clang++-12, clang++-13, clang++-14] + compiler: [g++-12, g++-13, g++-14, clang++-18, clang++-19, clang++-20] base-flags: ["", -DJINJA2CPP_CXX_STANDARD=17] build-config: [Release, Debug] build-shared: [TRUE, FALSE] include: - - compiler: g++-9 + - compiler: g++-12 extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF - - compiler: g++-10 + - compiler: g++-13 extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF - - compiler: g++-11 + - compiler: g++-14 extra-flags: -DJINJA2CPP_STRICT_WARNINGS=OFF + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup environment env: INPUT_COMPILER: ${{ matrix.compiler }} diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index b7a4635f..cc4c2233 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -19,34 +19,26 @@ on: jobs: windows-msvc-build: - runs-on: ${{matrix.run-machine}} + runs-on: windows-latest strategy: fail-fast: false max-parallel: 20 matrix: - compiler: [msvc-2019] base-flags: ["", -DJINJA2CPP_CXX_STANDARD=17] build-config: [Release, Debug] - build-platform: [x86, x64] + build-platform: [x64] build-runtime: ["", /MT, /MD] build-shared: [FALSE, TRUE] - include: - - compiler: msvc-2019 - build-platform: x86 - run-machine: windows-2019 - generator: Visual Studio 16 2019 - vc_vars: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat - - compiler: msvc-2019 - build-platform: x64 - run-machine: windows-2019 - generator: Visual Studio 16 2019 - vc_vars: C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat - - steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + + # Setup MSVC environment + - name: Setup MSVC + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.build-platform }} - name: Build shell: cmd @@ -58,23 +50,17 @@ jobs: INPUT_EXTRA_FLAGS: ${{ matrix.extra_flags }} INPUT_BUILD_PLATFORM: ${{ matrix.build-platform }} INPUT_BUILD_RUNTIME: ${{ matrix.build-runtime }} - INPUT_GENERATOR: ${{ matrix.generator }} - VC_VARS: "${{ matrix.vc_vars }}" run: | - call "%VC_VARS%" mkdir -p .build cd .build cmake --version - cmake .. -G "%INPUT_GENERATOR%" -DCMAKE_BUILD_TYPE=%INPUT_BUILD_CONFIG% -DJINJA2CPP_MSVC_RUNTIME_TYPE="%INPUT_BUILD_RUNTIME%" -DJINJA2CPP_DEPS_MODE=internal -DJINJA2CPP_BUILD_SHARED=%INPUT_BUILD_SHARED% %INPUT_BASE_FLAGS% %INPUT_EXTRA_FLAGS% + cmake .. -G "NMake Makefiles" -Dvariant_CONFIG_SELECT_VARIANT=variant_VARIANT_NONSTD -DCMAKE_BUILD_TYPE=%INPUT_BUILD_CONFIG% -DJINJA2CPP_MSVC_RUNTIME_TYPE="%INPUT_BUILD_RUNTIME%" -DJINJA2CPP_DEPS_MODE=internal -DJINJA2CPP_BUILD_SHARED=%INPUT_BUILD_SHARED% %INPUT_BASE_FLAGS% %INPUT_EXTRA_FLAGS% cmake --build . --config %INPUT_BUILD_CONFIG% --verbose - name: Test shell: cmd env: INPUT_BUILD_CONFIG: ${{ matrix.build-config }} - VC_VARS: "${{ matrix.vc_vars }}" run: | cd .build - call "%VC_VARS%" - set path=%BOOST_ROOT%\lib;%PATH% ctest -C %INPUT_BUILD_CONFIG% -V diff --git a/CMakeLists.txt b/CMakeLists.txt index 4aabcff5..d921ea8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.23.0) +cmake_minimum_required(VERSION 3.23.0..4.0) if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) @@ -19,9 +19,9 @@ set_property(CACHE JINJA2CPP_WITH_SANITIZERS PROPERTY STRINGS ${JINJA2CPP_SANITI set(JINJA2CPP_SUPPORTED_REGEX std boost) set(JINJA2CPP_USE_REGEX boost CACHE STRING "Use regex parser in lexer, boost works faster on most platforms") set_property(CACHE JINJA2CPP_USE_REGEX PROPERTY STRINGS ${JINJA2CPP_SUPPORTED_REGEX}) -set(JINJA2CPP_WITH_JSON_BINDINGS boost nlohmann rapid all none) -set(JINJA2CPP_WITH_JSON_BINDINGS boost CACHE STRING "Build with json support(boost|rapid)") -set_property(CACHE JINJA2CPP_WITH_JSON_BINDINGS PROPERTY STRINGS ${JINJA2CPP_WITH_JSON_BINDINGS}) +set(JINJA2CPP_WITH_JSON_SUPPORTED_BINDINGS boost nlohmann rapid) +set(JINJA2CPP_WITH_JSON_BINDINGS boost CACHE STRING "Build with json support(boost|rapid|nlohmann) for serialization operations, like tojson filter. 'boost' is default.") +set_property(CACHE JINJA2CPP_WITH_JSON_BINDINGS PROPERTY STRINGS ${JINJA2CPP_WITH_JSON_SUPPORTED_BINDINGS}) set (JINJA2CPP_DEPS_MODE "internal" CACHE STRING "Jinja2Cpp dependency management mode (internal | external | external-boost | conan-build). See documentation for details. 'interal' is default.") option(JINJA2CPP_BUILD_TESTS "Build Jinja2Cpp unit tests" ${JINJA2CPP_IS_MAIN_PROJECT}) option(JINJA2CPP_STRICT_WARNINGS "Enable additional warnings and treat them as errors" ON) @@ -144,13 +144,29 @@ include(collect_sources) set (LIB_TARGET_NAME jinja2cpp) -CollectSources(Sources Headers ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src) +set(JINJA2CPP_SOURCES + src/error_info.cpp + src/expression_evaluator.cpp + src/expression_parser.cpp + src/filesystem_handler.cpp + src/filters.cpp + src/generic_list.cpp + src/internal_value.cpp + src/lexer.cpp + src/serialize_filters.cpp + src/statements.cpp + src/string_converter_filter.cpp + src/template.cpp + src/template_env.cpp + src/template_parser.cpp + src/testers.cpp + src/value.cpp +) + CollectSources(PublicSources PublicHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/include) add_library(${LIB_TARGET_NAME} ${LIB_LINK_TYPE} - ${Sources} - ${Headers} - ${PublicHeaders} + ${JINJA2CPP_SOURCES} ) target_sources(${LIB_TARGET_NAME} @@ -185,19 +201,6 @@ endif() set(JINJA2CPP_PRIVATE_LIBS "${JINJA2CPP_PRIVATE_LIBS}") include(thirdparty/CMakeLists.txt) -target_link_libraries( - ${LIB_TARGET_NAME} - PUBLIC - ${JINJA2CPP_PUBLIC_LIBS} - PRIVATE - ${JINJA2CPP_PRIVATE_LIBS} -) - -target_include_directories(${LIB_TARGET_NAME} - PUBLIC - $ - $ -) if (JINJA2CPP_STRICT_WARNINGS) if (UNIX) @@ -218,19 +221,59 @@ endif () if ("${JINJA2CPP_USE_REGEX}" STREQUAL "boost") set(_regex_define "-DJINJA2CPP_USE_REGEX_BOOST") endif() + if ("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "boost") set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_BOOST") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/boost_json_serializer.cpp + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS Boost::json) elseif("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "rapid") set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/rapid_json_serializer.cpp + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS RapidJson) +else() + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN") + target_sources(${LIB_TARGET_NAME} + PRIVATE + src/binding/nlohmann_json_serializer.cpp + ) + list(APPEND JINJA2CPP_PRIVATE_LIBS nlohmann_json::nlohmann_json) endif() -target_compile_definitions(${LIB_TARGET_NAME} + +target_link_libraries( + ${LIB_TARGET_NAME} + PUBLIC + ${JINJA2CPP_PUBLIC_LIBS} + PRIVATE + ${JINJA2CPP_PRIVATE_LIBS} +) + +target_include_directories( + ${LIB_TARGET_NAME} + PUBLIC + $ + $ +) + +target_compile_definitions( + ${LIB_TARGET_NAME} PUBLIC -DBOOST_SYSTEM_NO_DEPRECATED -DBOOST_ERROR_CODE_HEADER_ONLY ${_regex_define} ${_bindings_define} ) - +# force nonstd variant option for msvc compiler +target_compile_definitions( + ${LIB_TARGET_NAME} + PUBLIC + $<$:variant_CONFIG_SELECT_VARIANT=variant_VARIANT_NONSTD> +) if (JINJA2CPP_BUILD_SHARED) target_compile_definitions(${LIB_TARGET_NAME} PRIVATE -DJINJA2CPP_BUILD_AS_SHARED PUBLIC -DJINJA2CPP_LINK_AS_SHARED) endif () @@ -251,8 +294,30 @@ if (JINJA2CPP_BUILD_TESTS) enable_testing() CollectSources(TestSources TestHeaders ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/test) - add_executable(jinja2cpp_tests ${TestSources} ${TestHeaders}) - target_link_libraries(jinja2cpp_tests gtest gtest_main + set(JINJA2CPP_TEST_SOURCES + test/basic_tests.cpp + test/errors_test.cpp + test/expressions_test.cpp + test/extends_test.cpp + test/filesystem_handler_test.cpp + test/filters_test.cpp + test/forloop_test.cpp + test/helpers_tests.cpp + test/if_test.cpp + test/import_test.cpp + test/includes_test.cpp + test/macro_test.cpp + test/metadata_test.cpp + test/perf_test.cpp + test/statements_tets.cpp + test/test_data + test/test_tools.h + test/testers_test.cpp + test/tojson_filter_test.cpp + test/user_callable_test.cpp + ) + add_executable(jinja2cpp_tests ${JINJA2CPP_TEST_SOURCES}) + target_link_libraries(jinja2cpp_tests gtest gmock gtest_main nlohmann_json::nlohmann_json ${LIB_TARGET_NAME} ${EXTRA_TEST_LIBS} ${JINJA2CPP_PRIVATE_LIBS}) set_target_properties(jinja2cpp_tests PROPERTIES @@ -277,6 +342,39 @@ if (JINJA2CPP_BUILD_TESTS) add_dependencies(jinja2cpp_tests CopyTestData) add_test(NAME jinja2cpp_tests COMMAND jinja2cpp_tests) + + if ("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "boost") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_BOOST") + set(_bindings_test_source test/binding/boost_json_binding_test.cpp) + + target_sources(jinja2cpp_tests + PRIVATE + src/binding/boost_json_serializer.cpp + ) + endif() + if("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "rapid") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(jinja2cpp_tests + PRIVATE + test/binding/rapid_json_binding_test.cpp + test/binding/rapid_json_serializer_test.cpp + ) + endif() + if("${JINJA2CPP_WITH_JSON_BINDINGS}" STREQUAL "nlohmann") + set(_bindings_define "-DJINJA2CPP_WITH_JSON_BINDINGS_RAPID") + target_sources(jinja2cpp_tests + PRIVATE + test/binding/nlohmann_json_binding_test.cpp + ) + endif() + target_compile_definitions(jinja2cpp_tests + PUBLIC + -DBOOST_SYSTEM_NO_DEPRECATED + -DBOOST_ERROR_CODE_HEADER_ONLY + ${_regex_define} + ${_bindings_define} + ) + endif () set (JINJA2CPP_INSTALL_CONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/${LIB_TARGET_NAME}") @@ -353,4 +451,4 @@ if(JINJA2CPP_INSTALL) DESTINATION ${JINJA2CPP_INSTALL_CONFIG_DIR} ) -endif() \ No newline at end of file +endif() diff --git a/cmake/patches/0001-fix-custom_command-error.patch b/cmake/patches/0001-fix-custom_command-error.patch new file mode 100644 index 00000000..5f4b2540 --- /dev/null +++ b/cmake/patches/0001-fix-custom_command-error.patch @@ -0,0 +1,24 @@ +From c5e3f11d98aa3116001361ef108c7bd4fa5e3a7f Mon Sep 17 00:00:00 2001 +Date: Fri, 13 Jun 2025 14:46:05 +0300 +Subject: [PATCH] fix custom_command error + +--- + CMakeLists.txt | 3 +-- + 1 file changed, 1 insertion(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index be860c93..73f067c9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -161,8 +161,7 @@ if(RAPIDJSON_BUILD_DOC) + add_subdirectory(doc) + endif() + +-add_custom_target(travis_doc) +-add_custom_command(TARGET travis_doc ++add_custom_target(TARGET travis_doc + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/travis-doxygen.sh) + + if(RAPIDJSON_BUILD_EXAMPLES) +-- + diff --git a/cmake/patches/rapid_json_improvements.patch b/cmake/patches/rapid_json_improvements.patch new file mode 100644 index 00000000..f5c2c997 --- /dev/null +++ b/cmake/patches/rapid_json_improvements.patch @@ -0,0 +1,428 @@ +From 82801edefa7c53648cfca119ea8dd7f8787106e0 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 17:50:54 +0200 +Subject: [PATCH 1/5] Use GNUInstallDirs defaults + +--- + CMakeLists.txt | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index be860c93d..7e4f799de 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -144,9 +144,11 @@ elseif (CMAKE_CXX_COMPILER_ID MATCHES "XL") + endif() + + #add extra search paths for libraries and includes +-SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The directory the headers are installed in") +-SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib" CACHE STRING "Directory where lib will install") +-SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${PROJECT_NAME}" CACHE PATH "Path to the documentation") ++include(GNUInstallDirs) ++ ++SET(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE PATH "The directory the headers are installed in") ++SET(LIB_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE STRING "Directory where lib will install") ++SET(DOC_INSTALL_DIR "${CMAKE_INSTALL_DOCDIR}" CACHE PATH "Path to the documentation") + + IF(UNIX OR CYGWIN) + SET(_CMAKE_INSTALL_DIR "${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME}") + +From 792928b9390bedf0fc4aaf934f76895308688c86 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 17:52:18 +0200 +Subject: [PATCH 2/5] Bump cmake policies + +--- + CMakeLists.txt | 2 +- + example/CMakeLists.txt | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 7e4f799de..c22a425c0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,4 +1,4 @@ +-CMAKE_MINIMUM_REQUIRED(VERSION 3.5) ++cmake_minimum_required(VERSION 3.5...4.0) + + SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) + +diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt +index 9f53c9aad..67024d060 100644 +--- a/example/CMakeLists.txt ++++ b/example/CMakeLists.txt +@@ -1,4 +1,4 @@ +-cmake_minimum_required(VERSION 2.8) ++cmake_minimum_required(VERSION 2.8...4.0) + + if(POLICY CMP0054) + cmake_policy(SET CMP0054 NEW) + +From 99bac253c925e7d47766f6beb54f14b37baba83f Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:05:33 +0200 +Subject: [PATCH 3/5] Use modern way to consume third-party gtest + +--- + .gitmodules | 3 --- + CMakeLists.txt | 3 +-- + CMakeModules/FindGTestSrc.cmake | 30 ------------------------- + test/CMakeLists.txt | 40 +++++++++++++++++++-------------- + test/perftest/CMakeLists.txt | 2 +- + test/perftest/perftest.h | 2 +- + test/unittest/CMakeLists.txt | 3 ++- + test/unittest/unittest.h | 2 +- + thirdparty/gtest | 1 - + 9 files changed, 29 insertions(+), 57 deletions(-) + delete mode 100644 .gitmodules + delete mode 100644 CMakeModules/FindGTestSrc.cmake + delete mode 160000 thirdparty/gtest + +diff --git a/.gitmodules b/.gitmodules +deleted file mode 100644 +index 5e41f7c97..000000000 +--- a/.gitmodules ++++ /dev/null +@@ -1,3 +0,0 @@ +-[submodule "thirdparty/gtest"] +- path = thirdparty/gtest +- url = https://github.com/google/googletest.git +diff --git a/CMakeLists.txt b/CMakeLists.txt +index c22a425c0..a8bffe2fd 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -20,8 +20,6 @@ SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + option(RAPIDJSON_BUILD_DOC "Build rapidjson documentation." ON) + option(RAPIDJSON_BUILD_EXAMPLES "Build rapidjson examples." ON) + option(RAPIDJSON_BUILD_TESTS "Build rapidjson perftests and unittests." ON) +-option(RAPIDJSON_BUILD_THIRDPARTY_GTEST +- "Use gtest installation in `thirdparty/gtest` by default if available" OFF) + + option(RAPIDJSON_BUILD_CXX11 "Build rapidjson with C++11" ON) + option(RAPIDJSON_BUILD_CXX17 "Build rapidjson with C++17" OFF) +@@ -172,6 +170,7 @@ if(RAPIDJSON_BUILD_EXAMPLES) + endif() + + if(RAPIDJSON_BUILD_TESTS) ++ enable_testing() + if(MSVC11) + # required for VS2012 due to missing support for variadic templates + add_definitions(-D_VARIADIC_MAX=10) +diff --git a/CMakeModules/FindGTestSrc.cmake b/CMakeModules/FindGTestSrc.cmake +deleted file mode 100644 +index f3cb8c990..000000000 +--- a/CMakeModules/FindGTestSrc.cmake ++++ /dev/null +@@ -1,30 +0,0 @@ +- +-SET(GTEST_SEARCH_PATH +- "${GTEST_SOURCE_DIR}" +- "${CMAKE_CURRENT_LIST_DIR}/../thirdparty/gtest/googletest") +- +-IF(UNIX) +- IF(RAPIDJSON_BUILD_THIRDPARTY_GTEST) +- LIST(APPEND GTEST_SEARCH_PATH "/usr/src/gtest") +- ELSE() +- LIST(INSERT GTEST_SEARCH_PATH 1 "/usr/src/gtest") +- ENDIF() +-ENDIF() +- +-FIND_PATH(GTEST_SOURCE_DIR +- NAMES CMakeLists.txt src/gtest_main.cc +- PATHS ${GTEST_SEARCH_PATH}) +- +- +-# Debian installs gtest include directory in /usr/include, thus need to look +-# for include directory separately from source directory. +-FIND_PATH(GTEST_INCLUDE_DIR +- NAMES gtest/gtest.h +- PATH_SUFFIXES include +- HINTS ${GTEST_SOURCE_DIR} +- PATHS ${GTEST_SEARCH_PATH}) +- +-INCLUDE(FindPackageHandleStandardArgs) +-find_package_handle_standard_args(GTestSrc DEFAULT_MSG +- GTEST_SOURCE_DIR +- GTEST_INCLUDE_DIR) +diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt +index 11c1b04c7..336dbb564 100644 +--- a/test/CMakeLists.txt ++++ b/test/CMakeLists.txt +@@ -1,20 +1,26 @@ +-find_package(GTestSrc) ++cmake_minimum_required(VERSION 3.11...4.0) + +-IF(GTESTSRC_FOUND) +- enable_testing() ++include(FetchContent) + +- if (WIN32 AND (NOT CYGWIN) AND (NOT MINGW)) +- set(gtest_disable_pthreads ON) +- set(gtest_force_shared_crt ON) +- endif() ++set(BUILD_GMOCK OFF) ++set(INSTALL_GTEST OFF) ++if (WIN32 AND (NOT CYGWIN) AND (NOT MINGW)) ++ set(gtest_disable_pthreads ON) ++ set(gtest_force_shared_crt ON) ++endif() ++set(fetchcontent_extra_args) ++if(CMAKE_VERSION GREATER_EQUAL 3.24) ++ list(APPEND fetchcontent_extra_args ++ FIND_PACKAGE_ARGS CONFIG ++ ) ++endif() ++FetchContent_Declare(GTest ++ GIT_REPOSITORY https://github.com/google/googletest ++ GIT_TAG v1.17.0 ++ ${fetchcontent_extra_args} ++) ++FetchContent_MakeAvailable(GTest) + +- add_subdirectory(${GTEST_SOURCE_DIR} ${CMAKE_BINARY_DIR}/googletest) +- include_directories(SYSTEM ${GTEST_INCLUDE_DIR}) +- +- set(TEST_LIBRARIES gtest gtest_main) +- +- add_custom_target(tests ALL) +- add_subdirectory(perftest) +- add_subdirectory(unittest) +- +-ENDIF(GTESTSRC_FOUND) ++add_custom_target(tests ALL) ++add_subdirectory(perftest) ++add_subdirectory(unittest) +diff --git a/test/perftest/CMakeLists.txt b/test/perftest/CMakeLists.txt +index 035e544d9..4d2582fa5 100644 +--- a/test/perftest/CMakeLists.txt ++++ b/test/perftest/CMakeLists.txt +@@ -6,7 +6,7 @@ set(PERFTEST_SOURCES + schematest.cpp) + + add_executable(perftest ${PERFTEST_SOURCES}) +-target_link_libraries(perftest ${TEST_LIBRARIES}) ++target_link_libraries(perftest PRIVATE GTest::gtest GTest::gtest_main) + + add_dependencies(tests perftest) + +diff --git a/test/perftest/perftest.h b/test/perftest/perftest.h +index 31e3ca633..8ceaebc7f 100644 +--- a/test/perftest/perftest.h ++++ b/test/perftest/perftest.h +@@ -52,7 +52,7 @@ + #pragma GCC diagnostic ignored "-Weffc++" + #endif + +-#include "gtest/gtest.h" ++#include + + #if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + #pragma GCC diagnostic pop +diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt +index 9a369d404..4f93438d4 100644 +--- a/test/unittest/CMakeLists.txt ++++ b/test/unittest/CMakeLists.txt +@@ -70,9 +70,10 @@ endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1") + + add_library(namespacetest STATIC namespacetest.cpp) ++target_link_libraries(namespacetest PUBLIC GTest::gtest GTest::gtest_main) + + add_executable(unittest ${UNITTEST_SOURCES}) +-target_link_libraries(unittest ${TEST_LIBRARIES} namespacetest) ++target_link_libraries(unittest PRIVATE namespacetest) + + add_dependencies(tests unittest) + +diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h +index 0e64d3970..0db961747 100644 +--- a/test/unittest/unittest.h ++++ b/test/unittest/unittest.h +@@ -44,7 +44,7 @@ + #pragma GCC diagnostic ignored "-Weffc++" + #endif + +-#include "gtest/gtest.h" ++#include + #include + + #if defined(__clang__) || defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) + +From b1ac740622aa087210fe98ff5c7bdedb5920b78f Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:09:29 +0200 +Subject: [PATCH 4/5] Use RapidJSON target internally + +--- + CMakeLists.txt | 7 ++++--- + test/perftest/CMakeLists.txt | 2 +- + test/unittest/CMakeLists.txt | 2 +- + 3 files changed, 6 insertions(+), 5 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index a8bffe2fd..492323ec9 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -155,8 +155,6 @@ ELSEIF(WIN32) + ENDIF() + SET(CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" CACHE PATH "The directory cmake files are installed in") + +-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) +- + if(RAPIDJSON_BUILD_DOC) + add_subdirectory(doc) + endif() +@@ -196,7 +194,10 @@ install(FILES readme.md + # Add an interface target to export it + add_library(RapidJSON INTERFACE) + +-target_include_directories(RapidJSON INTERFACE $) ++target_include_directories(RapidJSON INTERFACE ++ $ ++ $ ++) + + install(DIRECTORY include/rapidjson + DESTINATION "${INCLUDE_INSTALL_DIR}" +diff --git a/test/perftest/CMakeLists.txt b/test/perftest/CMakeLists.txt +index 4d2582fa5..e1677f862 100644 +--- a/test/perftest/CMakeLists.txt ++++ b/test/perftest/CMakeLists.txt +@@ -6,7 +6,7 @@ set(PERFTEST_SOURCES + schematest.cpp) + + add_executable(perftest ${PERFTEST_SOURCES}) +-target_link_libraries(perftest PRIVATE GTest::gtest GTest::gtest_main) ++target_link_libraries(perftest PRIVATE RapidJSON GTest::gtest GTest::gtest_main) + + add_dependencies(tests perftest) + +diff --git a/test/unittest/CMakeLists.txt b/test/unittest/CMakeLists.txt +index 4f93438d4..8a3c261c1 100644 +--- a/test/unittest/CMakeLists.txt ++++ b/test/unittest/CMakeLists.txt +@@ -70,7 +70,7 @@ endif() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DRAPIDJSON_HAS_STDSTRING=1") + + add_library(namespacetest STATIC namespacetest.cpp) +-target_link_libraries(namespacetest PUBLIC GTest::gtest GTest::gtest_main) ++target_link_libraries(namespacetest PUBLIC RapidJSON GTest::gtest GTest::gtest_main) + + add_executable(unittest ${UNITTEST_SOURCES}) + target_link_libraries(unittest PRIVATE namespacetest) + +From d18b42edcce14d8697f5dc5c777c76bfa6574e79 Mon Sep 17 00:00:00 2001 +From: Cristian Le +Date: Fri, 23 May 2025 18:21:51 +0200 +Subject: [PATCH 5/5] Use modern Config.cmake design + +--- + CMakeLists.txt | 59 +++++++++++---------------------- + RapidJSONConfig.cmake.in | 4 +-- + RapidJSONConfigVersion.cmake.in | 10 ------ + 3 files changed, 22 insertions(+), 51 deletions(-) + delete mode 100644 RapidJSONConfigVersion.cmake.in + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 492323ec9..062f6d80c 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -216,44 +216,25 @@ install(DIRECTORY example/ + # =============================== + + ################################################################################ +-# Export package for use from the build tree +-EXPORT( PACKAGE ${PROJECT_NAME} ) +- +-# Create the RapidJSONConfig.cmake file for other cmake projects. +-# ... for the build tree +-SET( CONFIG_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +-SET( CONFIG_DIR ${CMAKE_CURRENT_BINARY_DIR}) +-SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_SOURCE_DIR}/include" ) +- +-INCLUDE(CMakePackageConfigHelpers) +-CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake @ONLY ) +-CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}ConfigVersion.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake @ONLY) +- +-# ... for the install tree +-SET( CMAKECONFIG_INSTALL_DIR ${LIB_INSTALL_DIR}/cmake/${PROJECT_NAME} ) +-FILE( RELATIVE_PATH REL_INCLUDE_DIR +- "${CMAKECONFIG_INSTALL_DIR}" +- "${CMAKE_INSTALL_PREFIX}/include" ) +- +-SET( ${PROJECT_NAME}_INCLUDE_DIR "\${${PROJECT_NAME}_CMAKE_DIR}/${REL_INCLUDE_DIR}" ) +-SET( CONFIG_SOURCE_DIR ) +-SET( CONFIG_DIR ) +-CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in +- ${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake @ONLY ) +- +-INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${PROJECT_NAME}Config.cmake" +- DESTINATION ${CMAKECONFIG_INSTALL_DIR} ) ++include(CMakePackageConfigHelpers) ++write_basic_package_version_file(${PROJECT_NAME}ConfigVersion.cmake ++ COMPATIBILITY AnyNewerVersion ++) ++configure_package_config_file( ++ ${PROJECT_NAME}Config.cmake.in ++ ${PROJECT_NAME}Config.cmake ++ INSTALL_DESTINATION ${CMAKE_INSTALL_DIR} ++) + + # Install files +-IF(CMAKE_INSTALL_DIR) +- INSTALL(FILES +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake +- ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake +- DESTINATION "${CMAKE_INSTALL_DIR}" +- COMPONENT dev) +- +- INSTALL(TARGETS RapidJSON EXPORT RapidJSON-targets) +- INSTALL(EXPORT RapidJSON-targets DESTINATION ${CMAKE_INSTALL_DIR}) +-ENDIF() ++install(FILES ++ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" ++ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" ++ DESTINATION ${CMAKE_INSTALL_DIR} ++ COMPONENT dev ++) ++install(TARGETS RapidJSON EXPORT RapidJSON-targets) ++export(TARGETS RapidJSON ++ FILE RapidJSON-targets.cmake ++) ++install(EXPORT RapidJSON-targets DESTINATION ${CMAKE_INSTALL_DIR}) +diff --git a/RapidJSONConfig.cmake.in b/RapidJSONConfig.cmake.in +index 0ee1d9d3b..6a1e29397 100644 +--- a/RapidJSONConfig.cmake.in ++++ b/RapidJSONConfig.cmake.in +@@ -4,11 +4,11 @@ include ("${CMAKE_CURRENT_LIST_DIR}/RapidJSON-targets.cmake") + + ################################################################################ + # RapidJSON source dir +-set( RapidJSON_SOURCE_DIR "@CONFIG_SOURCE_DIR@") ++set( RapidJSON_SOURCE_DIR "RapidJSON_SOURCE_DIR-NOTFOUND") + + ################################################################################ + # RapidJSON build dir +-set( RapidJSON_DIR "@CONFIG_DIR@") ++set( RapidJSON_DIR "RapidJSON_DIR-NOTFOUND") + + ################################################################################ + # Compute paths +diff --git a/RapidJSONConfigVersion.cmake.in b/RapidJSONConfigVersion.cmake.in +deleted file mode 100644 +index 25741fc09..000000000 +--- a/RapidJSONConfigVersion.cmake.in ++++ /dev/null +@@ -1,10 +0,0 @@ +-SET(PACKAGE_VERSION "@LIB_VERSION_STRING@") +- +-IF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) +- SET(PACKAGE_VERSION_EXACT "true") +-ENDIF (PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) +-IF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +- SET(PACKAGE_VERSION_COMPATIBLE "true") +-ELSE (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) +- SET(PACKAGE_VERSION_UNSUITABLE "true") +-ENDIF (NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) diff --git a/include/jinja2cpp/binding/boost_json.h b/include/jinja2cpp/binding/boost_json.h index 93564694..3c6d4b91 100644 --- a/include/jinja2cpp/binding/boost_json.h +++ b/include/jinja2cpp/binding/boost_json.h @@ -104,14 +104,13 @@ struct BoostJsonArrayAccessor const IIndexBasedAccessor* GetIndexer() const override { return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { using Enum = Enumerator; auto j = this->GetValue(); if (!j) - return jinja2::ListEnumeratorPtr(); - - return jinja2::ListEnumeratorPtr(new Enum(j->begin(), j->end())); + return {}; + return jinja2::ListEnumeratorPtr{types::in_place_type_t{}, j->begin(), j->end()}; } Value GetItemByIndex(int64_t idx) const override diff --git a/include/jinja2cpp/binding/nlohmann_json.h b/include/jinja2cpp/binding/nlohmann_json.h index 88403780..5482ca67 100644 --- a/include/jinja2cpp/binding/nlohmann_json.h +++ b/include/jinja2cpp/binding/nlohmann_json.h @@ -62,7 +62,10 @@ class NLohmannJsonObjectAccessor : public IMapItemAccessor, public ReflectedData }; -struct NLohmannJsonArrayAccessor : IListItemAccessor, IIndexBasedAccessor, ReflectedDataHolder +struct NLohmannJsonArrayAccessor + : IListItemAccessor + , IIndexBasedAccessor + , ReflectedDataHolder { using ReflectedDataHolder::ReflectedDataHolder; @@ -77,14 +80,13 @@ struct NLohmannJsonArrayAccessor : IListItemAccessor, IIndexBasedAccessor, Refle return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { using Enum = Enumerator; auto j = this->GetValue(); if (!j) - return jinja2::ListEnumeratorPtr(); - - return jinja2::ListEnumeratorPtr(new Enum(j->begin(), j->end())); + return {}; + return jinja2::ListEnumeratorPtr{types::in_place_type_t{}, j->begin(), j->end()}; } Value GetItemByIndex(int64_t idx) const override diff --git a/include/jinja2cpp/binding/rapid_json.h b/include/jinja2cpp/binding/rapid_json.h index f29a1413..1a1aac86 100644 --- a/include/jinja2cpp/binding/rapid_json.h +++ b/include/jinja2cpp/binding/rapid_json.h @@ -108,14 +108,13 @@ struct RapidJsonArrayAccessor return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { using Enum = Enumerator::ConstValueIterator>; auto j = this->GetValue(); if (!j) - return jinja2::ListEnumeratorPtr(); - - return jinja2::ListEnumeratorPtr(new Enum(j->Begin(), j->End())); + return {}; + return jinja2::ListEnumeratorPtr{types::in_place_type_t{}, j->Begin(), j->End()}; } Value GetItemByIndex(int64_t idx) const override diff --git a/include/jinja2cpp/generic_list.h b/include/jinja2cpp/generic_list.h index 94360012..71e971b5 100644 --- a/include/jinja2cpp/generic_list.h +++ b/include/jinja2cpp/generic_list.h @@ -35,11 +35,6 @@ struct IIndexBasedAccessor : virtual IComparable struct IListEnumerator; using ListEnumeratorPtr = types::ValuePtr; -inline auto MakeEmptyListEnumeratorPtr() -{ - return ListEnumeratorPtr(); -} - /*! * \brief Generic list enumerator interface * @@ -132,7 +127,7 @@ struct IListItemAccessor : virtual IComparable * * @return Pointer to the enumerator of the list */ - virtual ListEnumeratorPtr CreateEnumerator() const = 0; + virtual nonstd::optional CreateEnumerator() const = 0; /*! * \brief Called to get size of the list if applicable. @@ -279,7 +274,7 @@ bool operator!=(const GenericList& lhs, const GenericList& rhs); template inline ListEnumeratorPtr IListItemAccessor::MakeEnumerator(Args&& ...args) { - return ListEnumeratorPtr(types::MakeValuePtr(std::forward(args)...)); + return ListEnumeratorPtr{types::in_place_type_t{}, std::forward(args)...}; } } // namespace jinja2 diff --git a/include/jinja2cpp/generic_list_impl.h b/include/jinja2cpp/generic_list_impl.h index 5ea5124a..8d10f6ed 100644 --- a/include/jinja2cpp/generic_list_impl.h +++ b/include/jinja2cpp/generic_list_impl.h @@ -90,7 +90,7 @@ struct InputIteratorListAccessor : IListItemAccessor return nullptr; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { return MakeEnumerator(&m_begin, &m_end ); } @@ -196,7 +196,7 @@ struct ForwardIteratorListAccessor : IListItemAccessor return nullptr; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { return MakeEnumerator(m_begin, m_end); } @@ -300,7 +300,7 @@ struct RandomIteratorListAccessor : IListItemAccessor, IIndexBasedAccessor return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { return MakeEnumerator(m_begin, m_end); } @@ -391,7 +391,7 @@ class GeneratedListAccessor : public IListItemAccessor return nullptr; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { return MakeEnumerator(&m_fn); } diff --git a/include/jinja2cpp/generic_list_iterator.h b/include/jinja2cpp/generic_list_iterator.h index 7af469f8..d678ab62 100644 --- a/include/jinja2cpp/generic_list_iterator.h +++ b/include/jinja2cpp/generic_list_iterator.h @@ -5,6 +5,8 @@ #include "value.h" #include "value_ptr.h" +#include + namespace jinja2 { namespace detail @@ -21,14 +23,14 @@ class JINJA2CPP_EXPORT GenericListIterator GenericListIterator() = default; - GenericListIterator(ListEnumeratorPtr enumerator) - : m_enumerator(types::ValuePtr(enumerator)) + GenericListIterator(nonstd::optional enumerator) + : m_enumerator{enumerator} { if (m_enumerator) - m_hasValue = m_enumerator->MoveNext(); + m_hasValue = (*m_enumerator)->MoveNext(); if (m_hasValue) - m_current = m_enumerator->GetCurrent(); + m_current = (*m_enumerator)->GetCurrent(); } bool operator == (const GenericListIterator& other) const @@ -37,7 +39,7 @@ class JINJA2CPP_EXPORT GenericListIterator return false; if (!m_enumerator && !other.m_enumerator) return true; - if (this->m_enumerator && other.m_enumerator && !m_enumerator->IsEqual(*other.m_enumerator)) + if (this->m_enumerator && other.m_enumerator && !(*m_enumerator)->IsEqual(*(*other.m_enumerator))) return false; if ((m_enumerator && !other.m_enumerator) || (!m_enumerator && other.m_enumerator)) return false; @@ -60,18 +62,17 @@ class JINJA2CPP_EXPORT GenericListIterator { if (!m_enumerator) return *this; - m_hasValue = m_enumerator->MoveNext(); + m_hasValue = (*m_enumerator)->MoveNext(); if (m_hasValue) { - m_current = m_enumerator->GetCurrent(); + m_current = (*m_enumerator)->GetCurrent(); } else { - EnumeratorPtr temp; Value tempVal; using std::swap; - swap(m_enumerator, temp); swap(m_current, tempVal); + m_enumerator.reset(); } return *this; @@ -93,7 +94,7 @@ class JINJA2CPP_EXPORT GenericListIterator } private: - EnumeratorPtr m_enumerator; + nonstd::optional m_enumerator; bool m_hasValue = false; Value m_current; }; diff --git a/include/jinja2cpp/polymorphic_value.h b/include/jinja2cpp/polymorphic_value.h deleted file mode 100644 index c5cb86ac..00000000 --- a/include/jinja2cpp/polymorphic_value.h +++ /dev/null @@ -1,449 +0,0 @@ -/* - -Copyright (c) 2016 Jonathan B. Coe - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -#ifndef ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED -#define ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED - -#include -#include -#include -#include -#include -#include - -// required variant_CPP17_OR_GREATER definition -#include - -// -// in_place: code duplicated in any-lite, expected-lite, optional-lite, value-ptr-lite, variant-lite: -// - -#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES -#define nonstd_lite_HAVE_IN_PLACE_TYPES 1 - -// C++17 std::in_place in : - -#if variant_CPP17_OR_GREATER - -#include - -namespace nonstd { - -using std::in_place; -using std::in_place_type; -using std::in_place_index; -using std::in_place_t; -using std::in_place_type_t; -using std::in_place_index_t; - -#define nonstd_lite_in_place_t( T) std::in_place_t -#define nonstd_lite_in_place_type_t( T) std::in_place_type_t -#define nonstd_lite_in_place_index_t(K) std::in_place_index_t - -#define nonstd_lite_in_place( T) std::in_place_t{} -#define nonstd_lite_in_place_type( T) std::in_place_type_t{} -#define nonstd_lite_in_place_index(K) std::in_place_index_t{} - -} // namespace nonstd - -#else // variant_CPP17_OR_GREATER - -#include - -namespace nonstd { -namespace detail { - -template< class T > -struct in_place_type_tag {}; - -template< std::size_t K > -struct in_place_index_tag {}; - -} // namespace detail - -struct in_place_t {}; - -template< class T > -inline in_place_t in_place( detail::in_place_type_tag = detail::in_place_type_tag() ) -{ - return in_place_t(); -} - -template< std::size_t K > -inline in_place_t in_place( detail::in_place_index_tag = detail::in_place_index_tag() ) -{ - return in_place_t(); -} - -template< class T > -inline in_place_t in_place_type( detail::in_place_type_tag = detail::in_place_type_tag() ) -{ - return in_place_t(); -} - -template< std::size_t K > -inline in_place_t in_place_index( detail::in_place_index_tag = detail::in_place_index_tag() ) -{ - return in_place_t(); -} - -// mimic templated typedef: - -#define nonstd_lite_in_place_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) -#define nonstd_lite_in_place_type_t( T) nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag ) -#define nonstd_lite_in_place_index_t(K) nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag ) - -#define nonstd_lite_in_place( T) nonstd::in_place_type -#define nonstd_lite_in_place_type( T) nonstd::in_place_type -#define nonstd_lite_in_place_index(K) nonstd::in_place_index - -} // namespace nonstd - -#endif // variant_CPP17_OR_GREATER -#endif // nonstd_lite_HAVE_IN_PLACE_TYPES - -namespace isocpp_p0201 { - -namespace detail { - -//////////////////////////////////////////////////////////////////////////// -// Implementation detail classes -//////////////////////////////////////////////////////////////////////////// - -template -struct default_copy { - T* operator()(const T& t) const { return new T(t); } -}; - -template -struct default_delete { - void operator()(const T* t) const { delete t; } -}; - -template -struct control_block { - virtual ~control_block() = default; - - virtual std::unique_ptr clone() const = 0; - - virtual T* ptr() = 0; -}; - -template -class direct_control_block : public control_block { - static_assert(!std::is_reference::value, ""); - U u_; - - public: - template - explicit direct_control_block(Ts&&... ts) : u_(U(std::forward(ts)...)) {} - - std::unique_ptr> clone() const override { - return std::make_unique(*this); - } - - T* ptr() override { return std::addressof(u_); } -}; - -template , - class D = default_delete> -class pointer_control_block : public control_block, public C { - std::unique_ptr p_; - - public: - explicit pointer_control_block(U* u, C c = C{}, D d = D{}) - : C(std::move(c)), p_(u, std::move(d)) {} - - explicit pointer_control_block(std::unique_ptr p, C c = C{}) - : C(std::move(c)), p_(std::move(p)) {} - - std::unique_ptr> clone() const override { - assert(p_); - return std::make_unique( - C::operator()(*p_), static_cast(*this), p_.get_deleter()); - } - - T* ptr() override { return p_.get(); } -}; - -template -class delegating_control_block : public control_block { - std::unique_ptr> delegate_; - - public: - explicit delegating_control_block(std::unique_ptr> b) - : delegate_(std::move(b)) {} - - std::unique_ptr> clone() const override { - return std::make_unique(delegate_->clone()); - } - - T* ptr() override { return delegate_->ptr(); } -}; - -} // end namespace detail - -class bad_polymorphic_value_construction : public std::exception { - public: - bad_polymorphic_value_construction() noexcept = default; - - const char* what() const noexcept override { - return "Dynamic and static type mismatch in polymorphic_value " - "construction"; - } -}; - -template -class polymorphic_value; - -template -struct is_polymorphic_value : std::false_type {}; - -template -struct is_polymorphic_value> : std::true_type {}; - -//////////////////////////////////////////////////////////////////////////////// -// `polymorphic_value` class definition -//////////////////////////////////////////////////////////////////////////////// - -template -class polymorphic_value { - static_assert(!std::is_union::value, ""); - static_assert(std::is_class::value, ""); - - template - friend class polymorphic_value; - - template - friend polymorphic_value make_polymorphic_value(Ts&&... ts); - template - friend polymorphic_value make_polymorphic_value(Ts&&... ts); - - T* ptr_ = nullptr; - std::unique_ptr> cb_; - - public: - // - // Destructor - // - - ~polymorphic_value() = default; - - // - // Constructors - // - - polymorphic_value() {} - - template , - class D = detail::default_delete, - class V = std::enable_if_t::value>> - explicit polymorphic_value(U* u, C copier = C{}, D deleter = D{}) { - if (!u) { - return; - } - -#ifndef ISOCPP_P0201_POLYMORPHIC_VALUE_NO_RTTI - if (std::is_same>::value && - std::is_same>::value && - typeid(*u) != typeid(U)) - throw bad_polymorphic_value_construction(); -#endif - std::unique_ptr p(u, std::move(deleter)); - - cb_ = std::make_unique>( - std::move(p), std::move(copier)); - ptr_ = u; - } - - // - // Copy-constructors - // - - polymorphic_value(const polymorphic_value& p) { - if (!p) { - return; - } - auto tmp_cb = p.cb_->clone(); - ptr_ = tmp_cb->ptr(); - cb_ = std::move(tmp_cb); - } - - // - // Move-constructors - // - - polymorphic_value(polymorphic_value&& p) noexcept { - ptr_ = p.ptr_; - cb_ = std::move(p.cb_); - p.ptr_ = nullptr; - } - - // - // Converting constructors - // - - template ::value && - std::is_convertible::value>> - explicit polymorphic_value(const polymorphic_value& p) { - polymorphic_value tmp(p); - ptr_ = tmp.ptr_; - cb_ = std::make_unique>( - std::move(tmp.cb_)); - } - - template ::value && - std::is_convertible::value>> - explicit polymorphic_value(polymorphic_value&& p) { - ptr_ = p.ptr_; - cb_ = std::make_unique>( - std::move(p.cb_)); - p.ptr_ = nullptr; - } - -#if __cplusplus < 201703L - -#endif - // - // In-place constructor - // - - template *, T*>::value && - !is_polymorphic_value>::value>, - class... Ts> - explicit polymorphic_value(nonstd_lite_in_place_type_t(U), Ts&&... ts) -// explicit polymorphic_value(std::in_place_type_t, Ts&&... ts) - : cb_(std::make_unique>( - std::forward(ts)...)) { - ptr_ = cb_->ptr(); - } - - - // - // Assignment - // - - polymorphic_value& operator=(const polymorphic_value& p) { - if (std::addressof(p) == this) { - return *this; - } - - if (!p) { - cb_.reset(); - ptr_ = nullptr; - return *this; - } - - auto tmp_cb = p.cb_->clone(); - ptr_ = tmp_cb->ptr(); - cb_ = std::move(tmp_cb); - return *this; - } - - // - // Move-assignment - // - - polymorphic_value& operator=(polymorphic_value&& p) noexcept { - if (std::addressof(p) == this) { - return *this; - } - - cb_ = std::move(p.cb_); - ptr_ = p.ptr_; - p.ptr_ = nullptr; - return *this; - } - - // - // Modifiers - // - - void swap(polymorphic_value& p) noexcept { - using std::swap; - swap(ptr_, p.ptr_); - swap(cb_, p.cb_); - } - - // - // Observers - // - - explicit operator bool() const { return bool(cb_); } - - const T* operator->() const { - assert(ptr_); - return ptr_; - } - - const T& operator*() const { - assert(*this); - return *ptr_; - } - - T* operator->() { - assert(*this); - return ptr_; - } - - T& operator*() { - assert(*this); - return *ptr_; - } -}; - -// -// polymorphic_value creation -// -template -polymorphic_value make_polymorphic_value(Ts&&... ts) { - polymorphic_value p; - p.cb_ = std::make_unique>( - std::forward(ts)...); - p.ptr_ = p.cb_->ptr(); - return p; -} -template -polymorphic_value make_polymorphic_value(Ts&&... ts) { - polymorphic_value p; - p.cb_ = std::make_unique>( - std::forward(ts)...); - p.ptr_ = p.cb_->ptr(); - return p; -} - -// -// non-member swap -// -template -void swap(polymorphic_value& t, polymorphic_value& u) noexcept { - t.swap(u); -} - -} // namespace isocpp_p0201 - -#endif // ISOCPP_P0201_POLYMORPHIC_VALUE_H_INCLUDED diff --git a/include/jinja2cpp/polymorphic_value/polymorphic.h b/include/jinja2cpp/polymorphic_value/polymorphic.h new file mode 100644 index 00000000..a32be6dc --- /dev/null +++ b/include/jinja2cpp/polymorphic_value/polymorphic.h @@ -0,0 +1,406 @@ +/* Copyright (c) 2016 The Value Types Authors. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==============================================================================*/ + +#ifndef XYZ_POLYMORPHIC_H_ +#define XYZ_POLYMORPHIC_H_ + +#include +#include +#include +#include +#include + +#ifndef XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS +#define XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS 1 +#endif // XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS + +namespace xyz { + +#ifndef XYZ_UNREACHABLE_DEFINED +#define XYZ_UNREACHABLE_DEFINED + +[[noreturn]] inline void unreachable() { // LCOV_EXCL_LINE +#if (__cpp_lib_unreachable >= 202202L) + std::unreachable(); // LCOV_EXCL_LINE +#elif defined(_MSC_VER) + __assume(false); // LCOV_EXCL_LINE +#else + __builtin_unreachable(); // LCOV_EXCL_LINE +#endif +} +#endif // XYZ_UNREACHABLE_DEFINED + +template > +class polymorphic { + struct control_block { + using allocator_traits = std::allocator_traits; + typename allocator_traits::pointer p_; + + virtual constexpr ~control_block() = default; + virtual constexpr void destroy(A& alloc) = 0; + virtual constexpr control_block* clone(const A& alloc) = 0; + virtual constexpr control_block* move(const A& alloc) = 0; + }; + + template + class direct_control_block final : public control_block { + union uninitialized_storage { + U u_; + + constexpr uninitialized_storage() {} + + constexpr ~uninitialized_storage() {} + } storage_; + + using cb_allocator = typename std::allocator_traits< + A>::template rebind_alloc>; + using cb_alloc_traits = std::allocator_traits; + + public: + template + constexpr direct_control_block(const A& alloc, Ts&&... ts) { + cb_allocator cb_alloc(alloc); + cb_alloc_traits::construct(cb_alloc, std::addressof(storage_.u_), + std::forward(ts)...); + control_block::p_ = std::addressof(storage_.u_); + } + + constexpr control_block* clone(const A& alloc) override { + cb_allocator cb_alloc(alloc); + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc, storage_.u_); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + constexpr control_block* move(const A& alloc) override { + cb_allocator cb_alloc(alloc); + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc, + std::move(storage_.u_)); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + constexpr void destroy(A& alloc) override { + cb_allocator cb_alloc(alloc); + cb_alloc_traits::destroy(cb_alloc, std::addressof(storage_.u_)); + cb_alloc_traits::deallocate(cb_alloc, this, 1); + } + }; + + control_block* cb_; + +#if defined(_MSC_VER) + // https://devblogs.microsoft.com/cppblog/msvc-cpp20-and-the-std-cpp20-switch/#msvc-extensions-and-abi + [[msvc::no_unique_address]] A alloc_; +#else + [[no_unique_address]] A alloc_; +#endif + + using allocator_traits = std::allocator_traits; + + template + [[nodiscard]] constexpr control_block* create_control_block( + Ts&&... ts) const { + using cb_allocator = typename std::allocator_traits< + A>::template rebind_alloc>; + cb_allocator cb_alloc(alloc_); + using cb_alloc_traits = std::allocator_traits; + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc_, + std::forward(ts)...); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + public: + using value_type = T; + using allocator_type = A; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + + // + // Constructors. + // + + explicit constexpr polymorphic() + requires std::default_initializable + : polymorphic(std::allocator_arg_t{}, A{}) { + static_assert(std::default_initializable && std::copy_constructible); + } + + template + constexpr explicit polymorphic(U&& u) + requires(!std::same_as>) && + std::copy_constructible> && + std::derived_from, T> && + std::default_initializable + : polymorphic(std::allocator_arg_t{}, A{}, std::forward(u)) {} + + template + explicit constexpr polymorphic(std::in_place_type_t, Ts&&... ts) + requires std::same_as, U> && + std::constructible_from && + std::copy_constructible && std::derived_from && + std::default_initializable + : polymorphic(std::allocator_arg_t{}, A{}, std::in_place_type, + std::forward(ts)...) {} + + template + explicit constexpr polymorphic(std::in_place_type_t, + std::initializer_list ilist, Ts&&... ts) + requires std::same_as, U> && + std::constructible_from, Ts&&...> && + std::copy_constructible && std::derived_from && + std::default_initializable + : polymorphic(std::allocator_arg_t{}, A{}, std::in_place_type, ilist, + std::forward(ts)...) {} + + constexpr polymorphic(const polymorphic& other) + : polymorphic(std::allocator_arg_t{}, + allocator_traits::select_on_container_copy_construction( + other.alloc_), + other) {} + + constexpr polymorphic(polymorphic&& other) noexcept( + allocator_traits::is_always_equal::value) + : polymorphic(std::allocator_arg_t{}, other.alloc_, std::move(other)) {} + + // + // Allocator-extended constructors. + // + + explicit constexpr polymorphic(std::allocator_arg_t, const A& alloc) + : alloc_(alloc) { + static_assert(std::default_initializable && std::copy_constructible); + + cb_ = create_control_block(); + } + + template + constexpr explicit polymorphic(std::allocator_arg_t, const A& alloc, U&& u) + requires(not std::same_as>) && + std::copy_constructible> && + std::derived_from, T> + : alloc_(alloc) { + cb_ = create_control_block>(std::forward(u)); + } + + template + explicit constexpr polymorphic(std::allocator_arg_t, const A& alloc, + std::in_place_type_t, Ts&&... ts) + requires std::same_as, U> && + std::constructible_from && + std::copy_constructible && std::derived_from + : alloc_(alloc) { + cb_ = create_control_block(std::forward(ts)...); + } + + template + explicit constexpr polymorphic(std::allocator_arg_t, const A& alloc, + std::in_place_type_t, + std::initializer_list ilist, Ts&&... ts) + requires std::same_as, U> && + std::constructible_from, Ts&&...> && + std::copy_constructible && std::derived_from + : alloc_(alloc) { + cb_ = create_control_block(ilist, std::forward(ts)...); + } + + constexpr polymorphic(std::allocator_arg_t, const A& alloc, + const polymorphic& other) + : alloc_(alloc) { + if (!other.valueless_after_move()) { + cb_ = other.cb_->clone(alloc_); + } else { + cb_ = nullptr; + } + } + + constexpr polymorphic( + std::allocator_arg_t, const A& alloc, + polymorphic&& other) noexcept(allocator_traits::is_always_equal::value) + : alloc_(alloc) { + if constexpr (allocator_traits::is_always_equal::value) { + cb_ = std::exchange(other.cb_, nullptr); + } else { + if (alloc_ == other.alloc_) { + cb_ = std::exchange(other.cb_, nullptr); + } else { + if (!other.valueless_after_move()) { + cb_ = other.cb_->move(alloc_); + } else { + cb_ = nullptr; + } + } + } + } + + // + // Destructor. + // + + constexpr ~polymorphic() { reset(); } + + // + // Assignment operators. + // + + constexpr polymorphic& operator=(const polymorphic& other) { + if (this == &other) return *this; + + // Check to see if the allocators need to be updated. + // We defer actually updating the allocator until later because it may be + // needed to delete the current control block. + bool update_alloc = + allocator_traits::propagate_on_container_copy_assignment::value; + + if (other.valueless_after_move()) { + reset(); + } else { + // Constructing a new control block could throw so we need to defer + // resetting or updating allocators until this is done. + + // Inlining the allocator into the construct_from call confuses LCOV. + auto tmp = other.cb_->clone(update_alloc ? other.alloc_ : alloc_); + reset(); + cb_ = tmp; + } + if (update_alloc) { + alloc_ = other.alloc_; + } + return *this; + } + + constexpr polymorphic& operator=(polymorphic&& other) noexcept( + allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value) { + if (this == &other) return *this; + + // Check to see if the allocators need to be updated. + // We defer actually updating the allocator until later because it may be + // needed to delete the current control block. + bool update_alloc = + allocator_traits::propagate_on_container_move_assignment::value; + + if (other.valueless_after_move()) { + reset(); + } else { + if (alloc_ == other.alloc_) { + std::swap(cb_, other.cb_); + other.reset(); + } else { + // Constructing a new control block could throw so we need to defer + // resetting or updating allocators until this is done. + + // Inlining the allocator into the construct_from call confuses LCOV. + auto tmp = other.cb_->move(update_alloc ? other.alloc_ : alloc_); + reset(); + cb_ = tmp; + } + } + + if (update_alloc) { + alloc_ = other.alloc_; + } + return *this; + } + + // + // Accessors. + // + + [[nodiscard]] constexpr pointer operator->() noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return cb_->p_; + } + + [[nodiscard]] constexpr const_pointer operator->() const noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return cb_->p_; + } + + [[nodiscard]] constexpr T& operator*() noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return *cb_->p_; + } + + [[nodiscard]] constexpr const T& operator*() const noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return *cb_->p_; + } + + [[nodiscard]] constexpr bool valueless_after_move() const noexcept { + return cb_ == nullptr; + } + + constexpr allocator_type get_allocator() const noexcept { return alloc_; } + + // + // Modifiers. + // + + constexpr void swap(polymorphic& other) noexcept( + std::allocator_traits::propagate_on_container_swap::value || + std::allocator_traits::is_always_equal::value) { + if constexpr (allocator_traits::propagate_on_container_swap::value) { + // If allocators move with their allocated objects, we can swap both. + std::swap(alloc_, other.alloc_); + std::swap(cb_, other.cb_); + return; + } else /* constexpr */ { + if (alloc_ == other.alloc_) { + std::swap(cb_, other.cb_); + } else { + unreachable(); // LCOV_EXCL_LINE + } + } + } + + friend constexpr void swap(polymorphic& lhs, polymorphic& rhs) noexcept( + noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); + } + + private: + constexpr void reset() noexcept { + if (cb_ != nullptr) { + cb_->destroy(alloc_); + cb_ = nullptr; + } + } +}; + +} // namespace xyz + +#endif // XYZ_POLYMORPHIC_H_ diff --git a/include/jinja2cpp/polymorphic_value/polymorphic_cxx14.h b/include/jinja2cpp/polymorphic_value/polymorphic_cxx14.h new file mode 100644 index 00000000..08492044 --- /dev/null +++ b/include/jinja2cpp/polymorphic_value/polymorphic_cxx14.h @@ -0,0 +1,476 @@ +/* Copyright (c) 2016 The Value Types Authors. All Rights Reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +==============================================================================*/ + +#ifndef XYZ_POLYMORPHIC_H_ +#define XYZ_POLYMORPHIC_H_ + +#include +#include +#include +#include +#include + +#ifndef XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS +#define XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS 1 +#endif // XYZ_POLYMORPHIC_HAS_EXTENDED_CONSTRUCTORS + +#ifndef XYZ_IN_PLACE_TYPE_DEFINED +#define XYZ_IN_PLACE_TYPE_DEFINED + +namespace xyz { +template +struct in_place_type_t {}; +} // namespace xyz +#endif // XYZ_IN_PLACE_TYPE_DEFINED + +#ifndef XYZ_UNREACHABLE_DEFINED +#define XYZ_UNREACHABLE_DEFINED + +namespace xyz { +[[noreturn]] inline void unreachable() { // LCOV_EXCL_LINE +#if (__cpp_lib_unreachable >= 202202L) + std::unreachable(); // LCOV_EXCL_LINE +#elif defined(_MSC_VER) + __assume(false); // LCOV_EXCL_LINE +#else + __builtin_unreachable(); // LCOV_EXCL_LINE +#endif +} +} // namespace xyz +#endif // XYZ_UNREACHABLE_DEFINED + +#ifndef XYZ_EMPTY_BASE_DEFINED +#define XYZ_EMPTY_BASE_DEFINED + +// This is a helper class to allow empty base class optimization. +// This implementation is duplicated in compatibility/in_place_type_cxx14.h. +// These implementations must be kept in sync. +// We duplicate implementations to allow this header to work as a single +// include. https://godbolt.org needs single-file includes. +namespace xyz { +namespace detail { +template ::value && !std::is_final::value> +class empty_base_optimization { + protected: + empty_base_optimization() = default; + + empty_base_optimization(const T& t) : t_(t) {} + + empty_base_optimization(T&& t) : t_(std::move(t)) {} + + T& get() noexcept { return t_; } + + const T& get() const noexcept { return t_; } + + T t_; +}; + +template +class empty_base_optimization : private T { + protected: + empty_base_optimization() = default; + + empty_base_optimization(const T& t) : T(t) {} + + empty_base_optimization(T&& t) : T(std::move(t)) {} + + T& get() noexcept { return *this; } + + const T& get() const noexcept { return *this; } +}; +} // namespace detail +} // namespace xyz +#endif // XYZ_EMPTY_BASE_DEFINED + +namespace xyz { + +template > +class polymorphic : private detail::empty_base_optimization { + struct control_block { + using allocator_traits = std::allocator_traits; + typename allocator_traits::pointer p_; + + virtual ~control_block() = default; + virtual void destroy(A& alloc) = 0; + virtual control_block* clone(const A& alloc) = 0; + virtual control_block* move(const A& alloc) = 0; + }; + + template + class direct_control_block final : public control_block { + union uninitialized_storage { + U u_; + + uninitialized_storage() {} + + ~uninitialized_storage() {} + } storage_; + + using cb_allocator = typename std::allocator_traits< + A>::template rebind_alloc>; + using cb_alloc_traits = std::allocator_traits; + + public: + template + direct_control_block(const A& alloc, Ts&&... ts) { + cb_allocator cb_alloc(alloc); + cb_alloc_traits::construct(cb_alloc, std::addressof(storage_.u_), + std::forward(ts)...); + control_block::p_ = std::addressof(storage_.u_); + } + + control_block* clone(const A& alloc) override { + cb_allocator cb_alloc(alloc); + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc, storage_.u_); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + control_block* move(const A& alloc) override { + cb_allocator cb_alloc(alloc); + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc, + std::move(storage_.u_)); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + void destroy(A& alloc) override { + cb_allocator cb_alloc(alloc); + cb_alloc_traits::destroy(cb_alloc, std::addressof(storage_.u_)); + cb_alloc_traits::deallocate(cb_alloc, this, 1); + } + }; + + control_block* cb_; + using allocator_traits = std::allocator_traits; + using alloc_base = detail::empty_base_optimization; + + template + control_block* create_control_block(Ts&&... ts) const { + using cb_allocator = typename std::allocator_traits< + A>::template rebind_alloc>; + cb_allocator cb_alloc(alloc_base::get()); + using cb_alloc_traits = std::allocator_traits; + auto mem = cb_alloc_traits::allocate(cb_alloc, 1); + try { + cb_alloc_traits::construct(cb_alloc, mem, alloc_base::get(), + std::forward(ts)...); + return mem; + } catch (...) { + cb_alloc_traits::deallocate(cb_alloc, mem, 1); + throw; + } + } + + public: + using value_type = T; + using allocator_type = A; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + + template ::value, + int>::type = 0> + polymorphic(std::allocator_arg_t, const A& alloc) : alloc_base(alloc) { + cb_ = create_control_block(); + } + + template ::value, + int>::type = 0, + typename AA = A, + typename std::enable_if::value, + int>::type = 0> + polymorphic() : alloc_base() { + cb_ = create_control_block(); + } + + template < + class U, class... Ts, + typename std::enable_if::value, + int>::type = 0, + typename std::enable_if::value, int>::type = + 0, + typename std::enable_if::value, int>::type = 0> + polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t, + Ts&&... ts) + : alloc_base(alloc) { + cb_ = create_control_block(std::forward(ts)...); + } + + template < + class U, class I, class... Ts, + typename std::enable_if< + std::is_constructible, Ts&&...>::value, + int>::type = 0, + typename std::enable_if::value, int>::type = + 0, + typename std::enable_if::value, int>::type = 0> + polymorphic(std::allocator_arg_t, const A& alloc, in_place_type_t, + std::initializer_list ilist, Ts&&... ts) + : alloc_base(alloc) { + cb_ = create_control_block(ilist, std::forward(ts)...); + } + + template < + class U, class I, class... Ts, + typename std::enable_if< + std::is_constructible, Ts&&...>::value, + int>::type = 0, + typename std::enable_if::value, int>::type = + 0, + typename std::enable_if::value, int>::type = 0, + typename AA = A, + typename std::enable_if::value, + int>::type = 0> + explicit polymorphic(in_place_type_t, std::initializer_list ilist, + Ts&&... ts) + : polymorphic(std::allocator_arg, A(), in_place_type_t{}, ilist, + std::forward(ts)...) {} + + template < + class U, class... Ts, + typename std::enable_if::value, + int>::type = 0, + typename std::enable_if::value, int>::type = + 0, + typename std::enable_if::value, int>::type = 0, + typename AA = A, + typename std::enable_if::value, + int>::type = 0> + explicit polymorphic(in_place_type_t, Ts&&... ts) + : polymorphic(std::allocator_arg, A(), in_place_type_t{}, + std::forward(ts)...) {} + + template < + class U, + typename std::enable_if< + !std::is_same::type>::type>::value, + int>::type = 0, + typename std::enable_if< + std::is_copy_constructible::type>::type>::value, + int>::type = 0, + typename std::enable_if< + std::is_base_of< + T, typename std::remove_cv< + typename std::remove_reference::type>::type>::value, + int>::type = 0> + explicit polymorphic(std::allocator_arg_t, const A& alloc, U&& u) + : polymorphic(std::allocator_arg_t{}, alloc, + in_place_type_t::type>::type>{}, + std::forward(u)) {} + + template < + class U, + typename std::enable_if< + !std::is_same::type>::type>::value, + int>::type = 0, + typename std::enable_if< + std::is_copy_constructible::type>::type>::value, + int>::type = 0, + typename std::enable_if< + std::is_base_of< + T, typename std::remove_cv< + typename std::remove_reference::type>::type>::value, + int>::type = 0> + explicit polymorphic(U&& u) + : polymorphic(std::allocator_arg_t{}, A{}, + in_place_type_t::type>::type>{}, + std::forward(u)) {} + + polymorphic(std::allocator_arg_t, const A& alloc, const polymorphic& other) + : alloc_base(alloc) { + if (!other.valueless_after_move()) { + cb_ = other.cb_->clone(alloc_base::get()); + } else { + cb_ = nullptr; + } + } + + polymorphic(const polymorphic& other) + : polymorphic(std::allocator_arg, + allocator_traits::select_on_container_copy_construction( + other.get_allocator()), + other) {} + + polymorphic( + std::allocator_arg_t, const A& alloc, + polymorphic&& other) noexcept(allocator_traits::is_always_equal::value) + : alloc_base(alloc) { + if (allocator_traits::propagate_on_container_copy_assignment::value) { + cb_ = other.cb_; + other.cb_ = nullptr; + } else { + if (get_allocator() == other.get_allocator()) { + cb_ = other.cb_; + other.cb_ = nullptr; + } else { + if (!other.valueless_after_move()) { + cb_ = other.cb_->move(alloc_base::get()); + } else { + cb_ = nullptr; + } + } + } + } + + polymorphic(polymorphic&& other) noexcept + : polymorphic(std::allocator_arg, other.get_allocator(), + std::move(other)) {} + + ~polymorphic() { reset(); } + + constexpr polymorphic& operator=(const polymorphic& other) { + if (this == &other) return *this; + + // Check to see if the allocators need to be updated. + // We defer actually updating the allocator until later because it may be + // needed to delete the current control block. + bool update_alloc = + allocator_traits::propagate_on_container_copy_assignment::value; + + if (other.valueless_after_move()) { + reset(); + } else { + // Constructing a new control block could throw so we need to defer + // resetting or updating allocators until this is done. + auto tmp = other.cb_->clone(update_alloc ? other.alloc_base::get() + : alloc_base::get()); + reset(); + cb_ = tmp; + } + if (update_alloc) { + alloc_base::get() = other.alloc_base::get(); + } + return *this; + } + + constexpr polymorphic& operator=(polymorphic&& other) noexcept( + allocator_traits::propagate_on_container_move_assignment::value || + allocator_traits::is_always_equal::value) { + if (this == &other) return *this; + + // Check to see if the allocators need to be updated. + // We defer actually updating the allocator until later because it may be + // needed to delete the current control block. + bool update_alloc = + allocator_traits::propagate_on_container_move_assignment::value; + + if (other.valueless_after_move()) { + reset(); + } else { + if (alloc_base::get() == other.alloc_base::get()) { + std::swap(cb_, other.cb_); + other.reset(); + } else { + // Constructing a new control block could throw so we need to defer + // resetting or updating allocators until this is done. + auto tmp = other.cb_->move(update_alloc ? other.alloc_base::get() + : alloc_base::get()); + reset(); + cb_ = tmp; + } + } + + if (update_alloc) { + alloc_base::get() = other.alloc_base::get(); + } + return *this; + } + + [[nodiscard]] pointer operator->() noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return cb_->p_; + } + + [[nodiscard]] const_pointer operator->() const noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return cb_->p_; + } + + [[nodiscard]] T& operator*() noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return *cb_->p_; + } + + [[nodiscard]] const T& operator*() const noexcept { + assert(!valueless_after_move()); // LCOV_EXCL_LINE + return *cb_->p_; + } + + [[nodiscard]] bool valueless_after_move() const noexcept { + return cb_ == nullptr; + } + + allocator_type get_allocator() const noexcept { return alloc_base::get(); } + + void swap(polymorphic& other) noexcept( + std::allocator_traits::propagate_on_container_swap::value || + std::allocator_traits::is_always_equal::value) { + if (allocator_traits::propagate_on_container_swap::value) { + // If allocators move with their allocated objects we can swap both. + std::swap(alloc_base::get(), other.alloc_base::get()); + std::swap(cb_, other.cb_); + return; + } else /* */ { + if (alloc_base::get() == other.alloc_base::get()) { + std::swap(cb_, other.cb_); + } else { + unreachable(); // LCOV_EXCL_LINE + } + } + } + + friend void swap(polymorphic& lhs, + polymorphic& rhs) noexcept(noexcept(lhs.swap(rhs))) { + lhs.swap(rhs); + } + + private: + void reset() noexcept { + if (cb_ != nullptr) { + cb_->destroy(alloc_base::get()); + cb_ = nullptr; + } + } +}; + +} // namespace xyz + +#endif // XYZ_POLYMORPHIC_H_ diff --git a/include/jinja2cpp/reflected_value.h b/include/jinja2cpp/reflected_value.h index b6ee12fb..a1ddb85b 100644 --- a/include/jinja2cpp/reflected_value.h +++ b/include/jinja2cpp/reflected_value.h @@ -203,6 +203,23 @@ struct Enumerator : public IListEnumerator , m_end(end) {} + Enumerator(const Enumerator& other) + : m_begin(other.m_begin) + , m_cur(other.m_cur) + , m_end(other.m_end) + , m_justInited(other.m_justInited) + { + } + + Enumerator(Enumerator&& other) noexcept + : m_begin(std::move(other.m_begin)) + , m_cur(std::move(other.m_cur)) + , m_end(std::move(other.m_end)) + , m_justInited(std::move(other.m_justInited)) + { + other.m_justInited = true; + } + void Reset() override { m_justInited = true; @@ -230,19 +247,12 @@ struct Enumerator : public IListEnumerator ListEnumeratorPtr Clone() const override { - auto result = std::make_unique>(m_begin, m_end); - result->m_cur = m_cur; - result->m_justInited = m_justInited; - return jinja2::ListEnumeratorPtr(result.release()); //, Deleter); + return jinja2::ListEnumeratorPtr(types::in_place_type_t{}, m_begin, m_end); } ListEnumeratorPtr Move() override { - auto result = std::make_unique>(m_begin, m_end); - result->m_cur = std::move(m_cur); - result->m_justInited = m_justInited; - this->m_justInited = true; - return jinja2::ListEnumeratorPtr(result.release()); //, Deleter); + return jinja2::ListEnumeratorPtr(types::in_place_type_t{}, std::move(*this)); } bool IsEqual(const IComparable& other) const override @@ -299,10 +309,10 @@ struct ContainerReflector return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { using Enum = Enumerator; - return jinja2::ListEnumeratorPtr(new Enum(m_value.begin(), m_value.end()));//, Enum::Deleter); + return jinja2::ListEnumeratorPtr{types::in_place_type_t{}, m_value.begin(), m_value.end()}; } Value GetItemByIndex(int64_t idx) const override @@ -319,7 +329,7 @@ struct ContainerReflector return false; auto enumerator = CreateEnumerator(); auto otherEnum = val->CreateEnumerator(); - if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum)) + if (enumerator && otherEnum && !(*enumerator)->IsEqual(**otherEnum)) return false; return true; } @@ -345,10 +355,10 @@ struct ContainerReflector return this; } - ListEnumeratorPtr CreateEnumerator() const override + nonstd::optional CreateEnumerator() const override { using Enum = Enumerator; - return jinja2::ListEnumeratorPtr(new Enum(m_value->begin(), m_value->end()));//, Enum::Deleter); + return jinja2::ListEnumeratorPtr{types::in_place_type_t{}, m_value->begin(), m_value->end()}; } Value GetItemByIndex(int64_t idx) const override @@ -365,7 +375,7 @@ struct ContainerReflector return false; auto enumerator = CreateEnumerator(); auto otherEnum = val->CreateEnumerator(); - if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum)) + if (enumerator && otherEnum && !(*enumerator)->IsEqual(**otherEnum)) return false; return true; } diff --git a/include/jinja2cpp/user_callable.h b/include/jinja2cpp/user_callable.h index dae15f3d..73044971 100644 --- a/include/jinja2cpp/user_callable.h +++ b/include/jinja2cpp/user_callable.h @@ -199,8 +199,6 @@ struct ParamUnwrapper template static auto& UnwrapRecursive(const RecWrapper& arg) { - if (!arg) - throw std::runtime_error("No value to unwrap"); return *arg; } diff --git a/include/jinja2cpp/value.h b/include/jinja2cpp/value.h index a22c33ac..27faf766 100644 --- a/include/jinja2cpp/value.h +++ b/include/jinja2cpp/value.h @@ -293,6 +293,24 @@ class Value : m_data(static_cast(val)) { } + /*! + * \brief Initializing constructor from the float value + * + * @param val Float value which should be used to initialize \ref Value instance + */ + Value(float val) + : m_data(static_cast(val)) + { + } + /*! + * \brief Initializing constructor from the double value + * + * @param val Double value which should be used to initialize \ref Value instance + */ + Value(double val) + : m_data(static_cast(val)) + { + } /*! * \brief Initializing constructor from the \ref ValuesList * @@ -707,12 +725,12 @@ bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr< inline Value::Value(const UserCallable& callable) - : m_data(types::MakeValuePtr(callable)) + : m_data(types::ValuePtr(xyz::in_place_type_t{}, callable)) { } inline Value::Value(UserCallable&& callable) - : m_data(types::MakeValuePtr(std::move(callable))) + : m_data(types::ValuePtr(std::move(callable))) { } @@ -742,19 +760,19 @@ inline Value& Value::operator=(Value&& val) noexcept return *this; } inline Value::Value(const ValuesMap& map) - : m_data(types::MakeValuePtr(map)) + : m_data(types::ValuePtr(types::in_place_type_t{}, map)) { } inline Value::Value(const ValuesList& list) - : m_data(types::MakeValuePtr(list)) + : m_data(types::ValuePtr(types::in_place_type_t{}, list)) { } inline Value::Value(ValuesList&& list) noexcept - : m_data(types::MakeValuePtr(std::move(list))) + : m_data(types::ValuePtr(std::move(list))) { } inline Value::Value(ValuesMap&& map) noexcept - : m_data(types::MakeValuePtr(std::move(map))) + : m_data(types::ValuePtr(std::move(map))) { } diff --git a/include/jinja2cpp/value_ptr.h b/include/jinja2cpp/value_ptr.h index 26eee6b6..650759a3 100644 --- a/include/jinja2cpp/value_ptr.h +++ b/include/jinja2cpp/value_ptr.h @@ -1,26 +1,25 @@ #ifndef JINJA2CPP_VALUE_PTR_H #define JINJA2CPP_VALUE_PTR_H -#include "polymorphic_value.h" +#include "jinja2cpp/polymorphic_value/polymorphic_cxx14.h" +#if __cplusplus == 201402L +#include "polymorphic_value/polymorphic_cxx14.h" +#else +#include "polymorphic_value/polymorphic.h" +#endif namespace jinja2 { namespace types { -using namespace isocpp_p0201; +using namespace xyz; template -using ValuePtr = polymorphic_value; +using ValuePtr = polymorphic; template -ValuePtr MakeValuePtr(Ts&&... ts) +auto MakeValuePtr(Ts&&... ts) { - return make_polymorphic_value(std::forward(ts)...); -} - -template -ValuePtr MakeValuePtr(Ts&&... ts) -{ - return make_polymorphic_value(std::forward(ts)...); + return polymorphic(xyz::in_place_type_t{}, std::forward(ts)...); } } // namespace types diff --git a/src/binding/boost_json_parser.h b/src/binding/boost_json_parser.h new file mode 100644 index 00000000..58d5cd75 --- /dev/null +++ b/src/binding/boost_json_parser.h @@ -0,0 +1,28 @@ +#ifndef JINJA2CPP_SRC_BOOST_JSON_PARSER_H +#define JINJA2CPP_SRC_BOOST_JSON_PARSER_H + +#include + +#include +#include + +namespace jinja2 +{ + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + //intentionally ignore metadataJson + (void)metadataJson; + boost::system::error_code ec; + auto value = boost::json::parse({json.data(), json.size()}, ec); + if (ec) + { + return nonstd::make_unexpected(ec.what()); + } + return Reflect(value); +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_BOOST_JSON_PARSER_H diff --git a/src/binding/boost_json_serializer.cpp b/src/binding/boost_json_serializer.cpp index 7dbabbbe..f23d5740 100644 --- a/src/binding/boost_json_serializer.cpp +++ b/src/binding/boost_json_serializer.cpp @@ -1,9 +1,14 @@ #include "boost_json_serializer.h" #include "../value_visitors.h" -#include + #include +#include +#include +#include + + template <> struct fmt::formatter : ostream_formatter {}; namespace jinja2 @@ -20,7 +25,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const ListAdapter& list) const { - boost::json::array listValue; //(boost::json::kind::array); + boost::json::array listValue; for (auto& v : list) { @@ -31,7 +36,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const MapAdapter& map) const { - boost::json::object mapNode; //(boost::json::kind::object); + boost::json::object mapNode; const auto& keys = map.GetKeys(); for (auto& k : keys) @@ -44,7 +49,7 @@ struct JsonInserter : visitors::BaseVisitor boost::json::value operator()(const KeyValuePair& kwPair) const { - boost::json::object pairNode; //(boost::json::kind::object); + boost::json::object pairNode; pairNode.emplace(kwPair.key.c_str(), Apply(kwPair.value)); return pairNode; @@ -101,19 +106,19 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j { switch (jv.kind()) { - case boost::json::kind::object: + case boost::json::kind::object: { fmt::format_to(std::back_inserter(os), "{}", '{'); if (indent != 0) { fmt::format_to(std::back_inserter(os), "{}", "\n"); } - const auto& obj = jv.get_object(); - if (!obj.empty()) - { - auto it = obj.begin(); - for (;;) - { + const auto& obj = jv.get_object(); + if (!obj.empty()) + { + auto it = obj.begin(); + for (;;) + { auto key = boost::json::serialize(it->key()); fmt::format_to( std::back_inserter(os), @@ -123,62 +128,73 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j ":", (indent == 0) ? 0 : 2 ); - PrettyPrint(os, it->value(), indent, level + 1); - if (++it == obj.end()) - break; + PrettyPrint(os, it->value(), indent, level + 1); + if (++it == obj.end()) + break; fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 2); - } - } + } + } if (indent != 0) { fmt::format_to(std::back_inserter(os), "{}", "\n"); } fmt::format_to(std::back_inserter(os), "{: >{}}", "}", (indent * level) + 1); - break; - } + break; + } - case boost::json::kind::array: + case boost::json::kind::array: { fmt::format_to(std::back_inserter(os), "["); - auto const& arr = jv.get_array(); - if (!arr.empty()) - { - auto it = arr.begin(); - for (;;) - { - PrettyPrint(os, *it, indent, level + 1); - if (++it == arr.end()) - break; - fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 2); - } - } + bool singleLineArray = false; + auto const& arr = jv.get_array(); + if (!arr.empty()) + { + if (!singleLineArray && indent != 0) + fmt::format_to(std::back_inserter(os), "\n"); + auto it = arr.begin(); + for (;;) + { + fmt::format_to(std::back_inserter(os), "{: >{}}", "", (indent * (level + 1))); + PrettyPrint(os, *it, indent, level + 1); + if (++it == arr.end()) + break; + fmt::format_to(std::back_inserter(os), "{: <{}}", ",", (indent == 0) ? 0 : 1); + if (!singleLineArray && indent != 0) + fmt::format_to(std::back_inserter(os), "\n"); + } + } + if (!singleLineArray && indent != 0) + { + fmt::format_to(std::back_inserter(os), "\n"); + fmt::format_to(std::back_inserter(os), "{: >{}}", "", (indent * level)); + } fmt::format_to(std::back_inserter(os), "]"); - break; - } + break; + } - case boost::json::kind::string: + case boost::json::kind::string: { fmt::format_to(std::back_inserter(os), "{}", boost::json::serialize(jv.get_string())); - break; - } + break; + } - case boost::json::kind::uint64: - case boost::json::kind::int64: - case boost::json::kind::double_: + case boost::json::kind::uint64: + case boost::json::kind::int64: + case boost::json::kind::double_: { fmt::format_to(std::back_inserter(os), "{}", jv); break; } - case boost::json::kind::bool_: + case boost::json::kind::bool_: { fmt::format_to(std::back_inserter(os), "{}", jv.get_bool()); - break; + break; } - case boost::json::kind::null: + case boost::json::kind::null: { fmt::format_to(std::back_inserter(os), "null"); - break; + break; } } } @@ -186,9 +202,40 @@ void PrettyPrint(fmt::basic_memory_buffer& os, const boost::json::value& j std::string ValueWrapper::AsString(const uint8_t indent) const { fmt::memory_buffer out; - PrettyPrint(out, m_value, indent); - return fmt::to_string(out); + PrettyPrint(out, m_value, indent); + return fmt::to_string(out); } } // namespace boost_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::literals; + boost_json_serializer::DocumentWrapper jsonDoc; + const auto jsonValue = jsonDoc.CreateValue(value); + const auto jsonString = jsonValue.AsString(static_cast(indent)); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + return result; +} + } // namespace jinja2 diff --git a/src/binding/boost_json_serializer.h b/src/binding/boost_json_serializer.h index 162333e4..934a2f84 100644 --- a/src/binding/boost_json_serializer.h +++ b/src/binding/boost_json_serializer.h @@ -41,7 +41,12 @@ class DocumentWrapper private: }; +using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; + } // namespace boost_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent); + } // namespace jinja2 #endif // JINJA2CPP_SRC_BOOST_JSON_SERIALIZER_H diff --git a/src/binding/nlohmann_json_parser.h b/src/binding/nlohmann_json_parser.h new file mode 100644 index 00000000..e3d15b2a --- /dev/null +++ b/src/binding/nlohmann_json_parser.h @@ -0,0 +1,30 @@ +#ifndef JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H +#define JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H + +#include + +#include +#include + +namespace jinja2 +{ + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + //intentionally ignore metadataJson + (void)metadataJson; + try + { + auto value = nlohmann::json::parse(json.data(), json.data() + json.size()); + return Reflect(value); + } + catch(std::exception& ex) + { + return nonstd::make_unexpected(ex.what()); + } +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_NLOHMANN_JSON_PARSER_H diff --git a/src/binding/nlohmann_json_serializer.cpp b/src/binding/nlohmann_json_serializer.cpp new file mode 100644 index 00000000..1b9d7405 --- /dev/null +++ b/src/binding/nlohmann_json_serializer.cpp @@ -0,0 +1,117 @@ +#include "nlohmann_json_serializer.h" + +#include "../value_visitors.h" + +#include + +#include +#include +#include + + +namespace jinja2 +{ +namespace nlohmann_json_serializer +{ +namespace +{ +struct JsonInserter : visitors::BaseVisitor +{ + using BaseVisitor::operator(); + + explicit JsonInserter() {} + + nlohmann::json operator()(const ListAdapter& list) const + { + nlohmann::json listValue; //(nlohmann::json::kind::array); + for (auto& v : list) + { + listValue.push_back(Apply(v)); + } + return listValue; + } + + nlohmann::json operator()(const MapAdapter& map) const + { + nlohmann::json mapNode; //(nlohmann::json::kind::object); + const auto& keys = map.GetKeys(); + for (auto& k : keys) + { + mapNode.emplace(k.c_str(), Apply(map.GetValueByName(k))); + } + + return mapNode; + } + + nlohmann::json operator()(const KeyValuePair& kwPair) const + { + nlohmann::json pairNode; //(nlohmann::json::kind::object); + pairNode.emplace(kwPair.key.c_str(), Apply(kwPair.value)); + return pairNode; + } + + nlohmann::json operator()(const std::string& str) const + { + return nlohmann::json(str); + } + + nlohmann::json operator()(const nonstd::string_view& str) const + { + return nlohmann::json(str); + } + + nlohmann::json operator()(const std::wstring& str) const + { + auto s = ConvertString(str); + return nlohmann::json(s); + } + + nlohmann::json operator()(const nonstd::wstring_view& str) const + { + auto s = ConvertString(str); + return nlohmann::json(s); + } + + nlohmann::json operator()(bool val) const { return nlohmann::json(val); } + + nlohmann::json operator()(EmptyValue) const { return nlohmann::json(); } + + nlohmann::json operator()(const Callable&) const { return nlohmann::json(""); } + + nlohmann::json operator()(double val) const { return nlohmann::json(val); } + + nlohmann::json operator()(int64_t val) const { return nlohmann::json(val); } +}; +} // namespace +} // namespace nlohmann_json_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::literals; + auto jsonValue = Apply(value); + auto jsonString = jsonValue.dump(indent == 0 ? -1 : indent); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + return result; +} + +} // namespace jinja2 diff --git a/src/binding/nlohmann_json_serializer.h b/src/binding/nlohmann_json_serializer.h new file mode 100644 index 00000000..81b1b199 --- /dev/null +++ b/src/binding/nlohmann_json_serializer.h @@ -0,0 +1,17 @@ +#ifndef JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H +#define JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H + +#include "../internal_value.h" + +#include + +#include + +namespace jinja2 +{ + +std::string ToJson(const InternalValue& value, uint8_t indent); + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_NLOHMANN_JSON_SERIALIZER_H diff --git a/src/binding/rapid_json_parser.h b/src/binding/rapid_json_parser.h new file mode 100644 index 00000000..0a3d85da --- /dev/null +++ b/src/binding/rapid_json_parser.h @@ -0,0 +1,72 @@ +#ifndef JINJA2CPP_SRC_RAPID_JSON_PARSER_H +#define JINJA2CPP_SRC_RAPID_JSON_PARSER_H + +#include +#include +#include + +#include + +#include +#include + + +namespace jinja2 +{ + +namespace detail +{ +template +struct RapidJsonEncodingType; + +template<> +struct RapidJsonEncodingType<1> +{ + using type = rapidjson::UTF8; +}; + +#ifdef BOOST_ENDIAN_BIG_BYTE +template<> +struct RapidJsonEncodingType<2> +{ + using type = rapidjson::UTF16BE; +}; + +template<> +struct RapidJsonEncodingType<4> +{ + using type = rapidjson::UTF32BE; +}; +#else +template<> +struct RapidJsonEncodingType<2> +{ + using type = rapidjson::UTF16LE; +}; + +template<> +struct RapidJsonEncodingType<4> +{ + using type = rapidjson::UTF32LE; +}; +#endif +} // namespace detail + +template +nonstd::expected Parse(nonstd::basic_string_view json, boost::anys::unique_any& metadataJson) +{ + using JsonDocumentType = rapidjson::GenericDocument::type>; + metadataJson.emplace(); // persist parse result + auto& jsonDoc = boost::any_cast(metadataJson); + rapidjson::ParseResult res = jsonDoc.Parse(json.data(), json.size()); + if (!res) + { + std::string jsonError = rapidjson::GetParseError_En(res.Code()); + return nonstd::make_unexpected(jsonError); + } + return Reflect(jsonDoc); +} + +} // namespace jinja2 + +#endif // JINJA2CPP_SRC_RAPID_JSON_PARSER_H diff --git a/src/binding/rapid_json_serializer.cpp b/src/binding/rapid_json_serializer.cpp index bd7e5a32..149b4d06 100644 --- a/src/binding/rapid_json_serializer.cpp +++ b/src/binding/rapid_json_serializer.cpp @@ -4,6 +4,8 @@ #include +#include + namespace jinja2 { namespace rapidjson_serializer @@ -116,12 +118,45 @@ std::string ValueWrapper::AsString(const uint8_t indent) const { PrettyWriter writer(buffer); writer.SetIndent(' ', indent); - writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); + //writer.SetFormatOptions(rapidjson::kFormatSingleLineArray); m_value.Accept(writer); } return buffer.GetString(); } + } // namespace rapidjson_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent) +{ + using namespace std::string_literals; + + rapidjson_serializer::DocumentWrapper jsonDoc; + const auto jsonValue = jsonDoc.CreateValue(value); + const auto jsonString = jsonValue.AsString(static_cast(indent)); + const auto result = std::accumulate(jsonString.begin(), jsonString.end(), ""s, [](const auto &str, const auto &c) + { + switch (c) + { + case '<': + return str + "\\u003c"; + break; + case '>': + return str +"\\u003e"; + break; + case '&': + return str +"\\u0026"; + break; + case '\'': + return str +"\\u0027"; + break; + default: + return str + c; + break; + } + }); + return result; +} + } // namespace jinja2 diff --git a/src/binding/rapid_json_serializer.h b/src/binding/rapid_json_serializer.h index 66e26c14..d5b4c4b5 100644 --- a/src/binding/rapid_json_serializer.h +++ b/src/binding/rapid_json_serializer.h @@ -10,6 +10,7 @@ namespace jinja2 { + namespace rapidjson_serializer { @@ -44,7 +45,13 @@ class DocumentWrapper std::shared_ptr m_document; }; +using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper; + + } // namespace rapidjson_serializer + +std::string ToJson(const InternalValue& value, uint8_t indent); + } // namespace jinja2 #endif // JINJA2CPP_SRC_RAPID_JSON_SERIALIZER_H diff --git a/src/error_info.cpp b/src/error_info.cpp index b60cc8e2..cb802995 100644 --- a/src/error_info.cpp +++ b/src/error_info.cpp @@ -102,7 +102,7 @@ struct formatter } template - auto format(const jinja2::Value& val, FormatContext& ctx) + auto format(const jinja2::Value& val, FormatContext& ctx) const { nonstd::visit(ValueRenderer(&ctx), val.data()); return fmt::format_to(ctx.out(), UNIVERSAL_STR("").GetValue()); @@ -126,53 +126,53 @@ void RenderErrorInfo(std::basic_string& result, const ErrorInfoTpl switch (errCode) { case ErrorCode::Unspecified: - format_to(std::back_inserter(out), UNIVERSAL_STR("Parse error").GetValue()); - break; + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Parse error").GetValue()); + break; case ErrorCode::UnexpectedException: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: {}").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected exception occurred during template processing. Exception: {}").GetValue(), extraParams[0]); break; } case ErrorCode::MetadataParseError: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Error occurred during template metadata parsing. Error: {}").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Error occurred during template metadata parsing. Error: {}").GetValue(), extraParams[0]); break; } case ErrorCode::YetUnsupported: - format_to(std::back_inserter(out), UNIVERSAL_STR("This feature has not been supported yet").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("This feature has not been supported yet").GetValue()); break; case ErrorCode::FileNotFound: - format_to(std::back_inserter(out), UNIVERSAL_STR("File not found").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("File not found").GetValue()); break; case ErrorCode::ExpectedStringLiteral: - format_to(std::back_inserter(out), UNIVERSAL_STR("String expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("String expected").GetValue()); break; case ErrorCode::ExpectedIdentifier: - format_to(std::back_inserter(out), UNIVERSAL_STR("Identifier expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Identifier expected").GetValue()); break; case ErrorCode::ExpectedSquareBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("']' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("']' expected").GetValue()); break; case ErrorCode::ExpectedRoundBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("')' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("')' expected").GetValue()); break; case ErrorCode::ExpectedCurlyBracket: - format_to(std::back_inserter(out), UNIVERSAL_STR("'}}' expected").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("'}}' expected").GetValue()); break; case ErrorCode::ExpectedToken: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token '{}'").GetValue(), extraParams[0]); if (extraParams.size() > 1) { - format_to(std::back_inserter(out), UNIVERSAL_STR(". Expected: ").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR(". Expected: ").GetValue()); for (std::size_t i = 1; i < extraParams.size(); ++ i) { if (i != 1) - format_to(std::back_inserter(out), UNIVERSAL_STR(", ").GetValue()); - format_to(std::back_inserter(out), UNIVERSAL_STR("\'{}\'").GetValue(), extraParams[i]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR(", ").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("\'{}\'").GetValue(), extraParams[i]); } } break; @@ -180,84 +180,84 @@ void RenderErrorInfo(std::basic_string& result, const ErrorInfoTpl case ErrorCode::ExpectedExpression: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected expression, got: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected expression, got: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::ExpectedEndOfStatement: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of statement, got: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of statement, got: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::ExpectedRawEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of raw block").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of raw block").GetValue()); break; case ErrorCode::ExpectedMetaEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of meta block").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Expected end of meta block").GetValue()); break; case ErrorCode::UnexpectedToken: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected token: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::UnexpectedStatement: { auto& extraParams = errInfo.GetExtraParams(); - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement: '{}'").GetValue(), extraParams[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement: '{}'").GetValue(), extraParams[0]); break; } case ErrorCode::UnexpectedCommentBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment begin").GetValue()); break; case ErrorCode::UnexpectedCommentEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected comment end").GetValue()); break; case ErrorCode::UnexpectedRawBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block begin").GetValue()); break; case ErrorCode::UnexpectedRawEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected raw block end").GetValue()); break; case ErrorCode::UnexpectedMetaBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block begin").GetValue()); break; case ErrorCode::UnexpectedMetaEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected meta block end").GetValue()); break; case ErrorCode::UnexpectedExprBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block begin").GetValue()); break; case ErrorCode::UnexpectedExprEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected expression block end").GetValue()); break; case ErrorCode::UnexpectedStmtBegin: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block begin").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block begin").GetValue()); break; case ErrorCode::UnexpectedStmtEnd: - format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block end").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Unexpected statement block end").GetValue()); break; case ErrorCode::TemplateNotParsed: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template not parsed").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template not parsed").GetValue()); break; case ErrorCode::TemplateNotFound: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template(s) not found: {}").GetValue(), errInfo.GetExtraParams()[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template(s) not found: {}").GetValue(), errInfo.GetExtraParams()[0]); break; case ErrorCode::InvalidTemplateName: - format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid template name: {}").GetValue(), errInfo.GetExtraParams()[0]); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid template name: {}").GetValue(), errInfo.GetExtraParams()[0]); break; case ErrorCode::InvalidValueType: - format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid value type").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Invalid value type").GetValue()); break; case ErrorCode::ExtensionDisabled: - format_to(std::back_inserter(out), UNIVERSAL_STR("Extension disabled").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Extension disabled").GetValue()); break; case ErrorCode::TemplateEnvAbsent: - format_to(std::back_inserter(out), UNIVERSAL_STR("Template environment doesn't set").GetValue()); + fmt::format_to(std::back_inserter(out), UNIVERSAL_STR("Template environment doesn't set").GetValue()); break; } format_to(std::back_inserter(out), UNIVERSAL_STR("\n{}").GetValue(), errInfo.GetLocationDescr()); - result = to_string(out); + result = {out.data(), out.size()}; } template<> diff --git a/src/filters.cpp b/src/filters.cpp index 3ab9f338..3700a600 100644 --- a/src/filters.cpp +++ b/src/filters.cpp @@ -1,6 +1,5 @@ #include "filters.h" -#include "binding/rapid_json_serializer.h" #include "generic_adapters.h" #include "out_stream.h" #include "testers.h" diff --git a/src/generic_adapters.h b/src/generic_adapters.h index a6493d2a..5c9b232c 100644 --- a/src/generic_adapters.h +++ b/src/generic_adapters.h @@ -116,7 +116,7 @@ class IndexedListItemAccessorImpl : public IListItemAccessor, public IIndexBased return this; } - ListEnumeratorPtr CreateEnumerator() const override; + nonstd::optional CreateEnumerator() const override; bool IsEqual(const IComparable& other) const override { @@ -125,7 +125,7 @@ class IndexedListItemAccessorImpl : public IListItemAccessor, public IIndexBased return false; auto enumerator = CreateEnumerator(); auto otherEnum = val->CreateEnumerator(); - if (enumerator && otherEnum && !enumerator->IsEqual(*otherEnum)) + if (!(*enumerator)->IsEqual(**otherEnum)) return false; return true; } @@ -155,19 +155,21 @@ class IndexedListAccessorImpl : public IListAccessor, public IndexedListItemAcce return result.value(); } - IListAccessorEnumerator* Clone() const override + nonstd::optional Clone() const override { - auto result = new Enumerator(this->m_list); - auto base = result; - base->m_curItem = this->m_curItem; + auto result = nonstd::make_optional(types::in_place_type_t{}, this->m_list); + auto base = *result; + Enumerator& typedBase = static_cast(*base); + typedBase.m_curItem = this->m_curItem; return result; } - IListAccessorEnumerator* Transfer() override + nonstd::optional Transfer() override { - auto result = new Enumerator(std::move(*this)); - auto base = result; - base->m_curItem = this->m_curItem; + auto result = nonstd::make_optional(types::in_place_type_t{}, std::move(*this)); + auto base = *result; + Enumerator& typedBase = static_cast(*base); + typedBase.m_curItem = this->m_curItem; this->m_list = nullptr; this->m_curItem = this->m_invalidIndex; this->m_maxItems = 0; @@ -179,7 +181,7 @@ class IndexedListAccessorImpl : public IListAccessor, public IndexedListItemAcce { return static_cast(this)->GetItemsCountImpl(); } - ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override; + nonstd::optional CreateListAccessorEnumerator() const override; }; template @@ -199,13 +201,13 @@ class MapAccessorImpl : public IMapAccessor, public MapItemAccessorImpl }; template -inline ListAccessorEnumeratorPtr IndexedListAccessorImpl::CreateListAccessorEnumerator() const +inline nonstd::optional IndexedListAccessorImpl::CreateListAccessorEnumerator() const { - return ListAccessorEnumeratorPtr(new Enumerator(this)); + return ListAccessorEnumeratorPtr(types::in_place_type_t{}, Enumerator(this)); } template -inline ListEnumeratorPtr IndexedListItemAccessorImpl::CreateEnumerator() const +inline nonstd::optional IndexedListItemAccessorImpl::CreateEnumerator() const { return MakeEnumerator(this); } diff --git a/src/internal_value.cpp b/src/internal_value.cpp index 3f64eeff..19698fe7 100644 --- a/src/internal_value.cpp +++ b/src/internal_value.cpp @@ -8,6 +8,37 @@ namespace jinja2 { +void InternalValue::SetParentData(const InternalValue& val) +{ + m_parentData = val.GetData(); +} + +void InternalValue::SetParentData(InternalValue&& val) +{ + m_parentData = std::move(val.GetData()); +} + +ListAdapter::Iterator::Iterator() +{ +} + +void ListAdapter::Iterator::increment() +{ + m_isFinished = !(*m_iterator)->MoveNext(); + ++ m_currentIndex; + m_currentVal = m_isFinished ? InternalValue() : (*m_iterator)->GetCurrent(); +} + +bool ListAdapter::Iterator::equal(const Iterator& other) const +{ + if (!this->m_iterator) + return !other.m_iterator ? true : other.equal(*this); + + if (!other.m_iterator) + return this->m_isFinished; + return (*this->m_iterator)->GetCurrent() == (*other.m_iterator)->GetCurrent() && this->m_currentIndex == other.m_currentIndex; +} + std::atomic_uint64_t UserCallable::m_gen{}; bool Value::IsEqual(const Value& rhs) const @@ -50,11 +81,7 @@ bool operator!=(const UserCallable& lhs, const UserCallable& rhs) bool operator==(const types::ValuePtr& lhs, const types::ValuePtr& rhs) { - if (lhs && rhs) - return *lhs == *rhs; - if ((lhs && !rhs) || (!lhs && rhs)) - return false; - return true; + return *lhs == *rhs; } bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr& rhs) @@ -64,11 +91,7 @@ bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr< bool operator==(const types::ValuePtr& lhs, const types::ValuePtr& rhs) { - if (lhs && rhs) - return *lhs == *rhs; - if ((lhs && !rhs) || (!lhs && rhs)) - return false; - return true; + return *lhs == *rhs; } bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr& rhs) @@ -78,11 +101,7 @@ bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr& lhs, const types::ValuePtr& rhs) { - if (lhs && rhs) - return *lhs == *rhs; - if ((lhs && !rhs) || (!lhs && rhs)) - return false; - return true; + return *lhs == *rhs; } bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr& rhs) @@ -92,11 +111,7 @@ bool operator!=(const types::ValuePtr& lhs, const types::ValuePtr& bool operator==(const types::ValuePtr>& lhs, const types::ValuePtr>& rhs) { - if (lhs && rhs) - return *lhs == *rhs; - if ((lhs && !rhs) || (!lhs && rhs)) - return false; - return true; + return *lhs == *rhs; } bool operator!=(const types::ValuePtr>& lhs, const types::ValuePtr>& rhs) @@ -394,9 +409,9 @@ class GenericListAdapter : public IListAccessor public: struct Enumerator : public IListAccessorEnumerator { - ListEnumeratorPtr m_enum; + nonstd::optional m_enum; - explicit Enumerator(ListEnumeratorPtr e) + explicit Enumerator(nonstd::optional e) : m_enum(std::move(e)) { } @@ -405,18 +420,24 @@ class GenericListAdapter : public IListAccessor void Reset() override { if (m_enum) - m_enum->Reset(); + (*m_enum)->Reset(); + } + bool MoveNext() override { return !m_enum ? false : (*m_enum)->MoveNext(); } + InternalValue GetCurrent() const override { return !m_enum ? InternalValue() : Value2IntValue((*m_enum)->GetCurrent()); } + nonstd::optional Clone() const override + { + return !m_enum ? nonstd::optional{} : nonstd::make_optional(types::in_place_type_t{}, (*m_enum)->Clone()); + } + nonstd::optional Transfer() override + { + return nonstd::make_optional(types::in_place_type_t{}, std::move(*m_enum)); } - bool MoveNext() override { return !m_enum ? false : m_enum->MoveNext(); } - InternalValue GetCurrent() const override { return !m_enum ? InternalValue() : Value2IntValue(m_enum->GetCurrent()); } - IListAccessorEnumerator* Clone() const override { return !m_enum ? new Enumerator(MakeEmptyListEnumeratorPtr()) : new Enumerator(m_enum->Clone()); } - IListAccessorEnumerator* Transfer() override { return new Enumerator(std::move(m_enum)); } bool IsEqual(const IComparable& other) const override { auto* val = dynamic_cast(&other); if (!val) return false; - if (m_enum && val->m_enum && !m_enum->IsEqual(*val->m_enum)) + if (m_enum && val->m_enum && !(*m_enum)->IsEqual(**val->m_enum)) return false; if ((m_enum && !val->m_enum) || (!m_enum && val->m_enum)) return false; @@ -442,12 +463,12 @@ class GenericListAdapter : public IListAccessor return visit(visitors::InputValueConvertor(true, false), std::move(val.data())).get(); } bool ShouldExtendLifetime() const override { return m_values.ShouldExtendLifetime(); } - ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override + nonstd::optional CreateListAccessorEnumerator() const override { const IListItemAccessor* accessor = m_values.Get().GetAccessor(); if (!accessor) - return ListAccessorEnumeratorPtr(new Enumerator(MakeEmptyListEnumeratorPtr())); - return ListAccessorEnumeratorPtr(new Enumerator(m_values.Get().GetAccessor()->CreateEnumerator())); + return {}; + return ListAccessorEnumeratorPtr(Enumerator(m_values.Get().GetAccessor()->CreateEnumerator())); } GenericList CreateGenericList() const override { @@ -548,6 +569,18 @@ ListAdapter ListAdapter::CreateAdapter(std::function Clone() const override { - auto result = new Enumerator(*this); - return result; + return nonstd::make_optional(types::in_place_type_t{}, *this); } - IListAccessorEnumerator* Transfer() override + nonstd::optional Transfer() override { - auto result = new Enumerator(std::move(*this)); - return result; + return nonstd::make_optional(types::in_place_type_t{}, std::move(*this)); } bool IsEqual(const IComparable& other) const override @@ -607,7 +638,7 @@ ListAdapter ListAdapter::CreateAdapter(std::function GetSize() const override { return nonstd::optional(); } nonstd::optional GetItem(int64_t /*idx*/) const override { return nonstd::optional(); } bool ShouldExtendLifetime() const override { return false; } - ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const override { return ListAccessorEnumeratorPtr(new Enumerator(&m_fn)); } + nonstd::optional CreateListAccessorEnumerator() const override { return ListAccessorEnumeratorPtr(types::in_place_type_t{}, Enumerator(&m_fn)); } GenericList CreateGenericList() const override { @@ -660,18 +691,18 @@ auto CreateIndexedSubscribedList(Holder&& holder, const InternalValue& subscript template auto CreateGenericSubscribedList(Holder&& holder, const InternalValue& subscript) { - return ListAdapter::CreateAdapter([h = std::forward(holder), e = ListAccessorEnumeratorPtr(), isFirst = true, isLast = false, subscript]() mutable { + return ListAdapter::CreateAdapter([h = std::forward(holder), e = nonstd::optional(), isFirst = true, isLast = false, subscript]() mutable { using ResultType = nonstd::optional; if (isFirst) { e = h.Get().GetEnumerator(); - isLast = !e->MoveNext(); + isLast = !(*e)->MoveNext(); isFirst = false; } if (isLast) return ResultType(); - return ResultType(Subscript(e->GetCurrent(), subscript, nullptr)); + return ResultType(Subscript((*e)->GetCurrent(), subscript, nullptr)); }); } diff --git a/src/internal_value.h b/src/internal_value.h index b674db1c..add468c9 100644 --- a/src/internal_value.h +++ b/src/internal_value.h @@ -1,8 +1,9 @@ #ifndef JINJA2CPP_SRC_INTERNAL_VALUE_H #define JINJA2CPP_SRC_INTERNAL_VALUE_H +#include "jinja2cpp/config.h" #include -//#include +#include #include #include @@ -16,7 +17,7 @@ #include "robin_hood.h" #endif - +#include #include #include @@ -52,7 +53,7 @@ class ReferenceWrapper T* m_ptr; }; -template +template class RecursiveWrapper { public: @@ -180,6 +181,8 @@ struct IsRecursive : std::true_type {}; template<> struct IsRecursive : std::true_type {}; +struct IListAccessorEnumerator; +using ListAccessorEnumeratorPtr = types::ValuePtr; struct IListAccessorEnumerator : virtual IComparable { virtual ~IListAccessorEnumerator() {} @@ -189,9 +192,9 @@ struct IListAccessorEnumerator : virtual IComparable virtual bool MoveNext() = 0; virtual InternalValue GetCurrent() const = 0; - virtual IListAccessorEnumerator* Clone() const = 0; - virtual IListAccessorEnumerator* Transfer() = 0; -/* + virtual nonstd::optional Clone() const = 0; + virtual nonstd::optional Transfer() = 0; +/* struct Cloner { Cloner() = default; @@ -217,7 +220,7 @@ struct IListAccessor virtual nonstd::optional GetSize() const = 0; virtual nonstd::optional GetItem(int64_t idx) const = 0; - virtual ListAccessorEnumeratorPtr CreateListAccessorEnumerator() const = 0; + virtual nonstd::optional CreateListAccessorEnumerator() const = 0; virtual GenericList CreateGenericList() const = 0; virtual bool ShouldExtendLifetime() const = 0; }; @@ -287,7 +290,7 @@ class ListAdapter return GenericList(); } - ListAccessorEnumeratorPtr GetEnumerator() const; + nonstd::optional GetEnumerator() const; class Iterator; @@ -370,6 +373,22 @@ class InternalValue public: InternalValue() = default; + + InternalValue(bool val) + : m_data(InternalValueData(val)) + { + } + + InternalValue(int64_t val) + : m_data(InternalValueData(val)) + { + } + + InternalValue(double val) + : m_data(InternalValueData(val)) + { + } + template InternalValue(T&& val, typename std::enable_if, InternalValue>::value>::type* = nullptr) : m_data(InternalValueData(std::forward(val))) @@ -382,15 +401,9 @@ class InternalValue auto& GetParentData() {return m_parentData;} auto& GetParentData() const {return m_parentData;} - void SetParentData(const InternalValue& val) - { - m_parentData = val.GetData(); - } + void SetParentData(const InternalValue& val); - void SetParentData(InternalValue&& val) - { - m_parentData = std::move(val.GetData()); - } + void SetParentData(InternalValue&& val); bool ShouldExtendLifetime() const { @@ -426,52 +439,34 @@ inline bool operator!=(const InternalValue& lhs, const InternalValue& rhs) return !(lhs == rhs); } -class ListAdapter::Iterator +class JINJA2CPP_EXPORT ListAdapter::Iterator : public boost::iterator_facade< Iterator, const InternalValue, boost::forward_traversal_tag> { public: - Iterator() = default; + Iterator(); - explicit Iterator(ListAccessorEnumeratorPtr&& iter) + explicit Iterator(nonstd::optional&& iter) : m_iterator(std::move(iter)) - , m_isFinished(!m_iterator->MoveNext()) - , m_currentVal(m_isFinished ? InternalValue() : m_iterator->GetCurrent()) + , m_isFinished(m_iterator ? !(*m_iterator)->MoveNext() : true) + , m_currentVal(m_isFinished ? InternalValue() : (*m_iterator)->GetCurrent()) {} private: friend class boost::iterator_core_access; - void increment() - { - m_isFinished = !m_iterator->MoveNext(); - ++ m_currentIndex; - m_currentVal = m_isFinished ? InternalValue() : m_iterator->GetCurrent(); - } + void increment(); - bool equal(const Iterator& other) const - { - if (!this->m_iterator) - return !other.m_iterator ? true : other.equal(*this); - - if (!other.m_iterator) - return this->m_isFinished; -// return true; - //const InternalValue& lhs = *(this->m_iterator); - //const InternalValue& rhs = *(other.m_iterator); - //return lhs == rhs; - return this->m_iterator->GetCurrent() == other.m_iterator->GetCurrent() && this->m_currentIndex == other.m_currentIndex; - ///return *(this->m_iterator) == *(other.m_iterator) && this->m_currentIndex == other.m_currentIndex; - } + bool equal(const Iterator& other) const; const InternalValue& dereference() const { return m_currentVal; } - ListAccessorEnumeratorPtr m_iterator; + nonstd::optional m_iterator; bool m_isFinished = true; mutable uint64_t m_currentIndex = 0; mutable InternalValue m_currentVal; @@ -564,7 +559,7 @@ inline InternalValue MapAdapter::GetValueByName(const std::string& name) const return InternalValue(); } -inline ListAccessorEnumeratorPtr ListAdapter::GetEnumerator() const {return m_accessorProvider()->CreateListAccessorEnumerator();} +inline nonstd::optional ListAdapter::GetEnumerator() const {return {m_accessorProvider()->CreateListAccessorEnumerator()};} inline ListAdapter::Iterator ListAdapter::begin() const {return Iterator(m_accessorProvider()->CreateListAccessorEnumerator());} inline ListAdapter::Iterator ListAdapter::end() const {return Iterator();} @@ -639,6 +634,7 @@ class Callable CallableHolder m_callable; }; + inline bool IsEmpty(const InternalValue& val) { return val.IsEmpty() || nonstd::get_if(&val.GetData()) != nullptr; diff --git a/src/lexer.cpp b/src/lexer.cpp index dfd33eb3..a59fdf39 100644 --- a/src/lexer.cpp +++ b/src/lexer.cpp @@ -1,7 +1,5 @@ #include "lexer.h" -#include - namespace jinja2 { diff --git a/src/lexer.h b/src/lexer.h index e644de2f..9a1fd3fe 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -107,7 +107,7 @@ struct Token ExprBegin, ExprEnd, }; - + Type type = Unknown; CharRange range = {0, 0}; InternalValue value; @@ -202,7 +202,7 @@ class Lexer { return m_tokens; } - + auto GetHelper() const {return m_helper;} private: @@ -317,12 +317,12 @@ class LexScanner return EatIfEqualImpl(tok, [type](const Token& t) {return t.type == type;}); } - + auto GetAsKeyword(const Token& tok) const { return m_helper->GetKeyword(tok.range); } - + bool EatIfEqual(Keyword kwType, Token* tok = nullptr) { if (m_state.m_cur == m_state.m_end) @@ -330,7 +330,7 @@ class LexScanner return EatIfEqualImpl(tok, [this, kwType](const Token& t) {return GetAsKeyword(t) == kwType;}); } - + private: template bool EatIfEqualImpl(Token* tok, Fn&& predicate) @@ -349,7 +349,7 @@ class LexScanner private: State m_state; LexerHelper* m_helper; - + static const Token& EofToken() { static Token eof; diff --git a/src/out_stream.h b/src/out_stream.h index 8e3f001b..554a8a81 100644 --- a/src/out_stream.h +++ b/src/out_stream.h @@ -4,7 +4,6 @@ #include "internal_value.h" #include -#include #include namespace jinja2 diff --git a/src/renderer.h b/src/renderer.h index 9c08d237..cda9e860 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/src/serialize_filters.cpp b/src/serialize_filters.cpp index a8e4363a..8bf3b825 100644 --- a/src/serialize_filters.cpp +++ b/src/serialize_filters.cpp @@ -15,10 +15,10 @@ #ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST #include "binding/boost_json_serializer.h" -using DocumentWrapper = jinja2::boost_json_serializer::DocumentWrapper; +#elif JINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN +#include "binding/nlohmann_json_serializer.h" #else #include "binding/rapid_json_serializer.h" -using DocumentWrapper = jinja2::rapidjson_serializer::DocumentWrapper; #endif @@ -149,25 +149,7 @@ InternalValue Serialize::Filter(const InternalValue& value, RenderContext& conte if (m_mode == JsonMode) { const auto indent = ConvertToInt(this->GetArgumentValue("indent", context)); - DocumentWrapper jsonDoc; - const auto jsonValue = jsonDoc.CreateValue(value); - const auto jsonString = jsonValue.AsString(static_cast(indent)); - std::string result = ""s; - result.reserve(jsonString.size()); - for (char c : jsonString) { - if (c == '<') { - result.append("\\u003c"); - } else if (c == '>') { - result.append("\\u003e"); - } else if (c == '&') { - result.append("\\u0026"); - } else if (c == '\'') { - result.append("\\u0027"); - } else { - result.push_back(c); - } - } - return result; + return ToJson(value, indent); } return InternalValue(); @@ -233,10 +215,10 @@ struct FormatArgumentConverter : visitors::BaseVisitor { m_store.push_back(fmt::arg(m_name.c_str(), t)); } - return fmt::detail::make_arg(t); + return fmt::basic_format_arg(t); } - const RenderContext* m_context; + const RenderContext* m_context{}; FormatDynamicArgsStore& m_store; const std::string m_name; bool m_named = false; @@ -427,8 +409,8 @@ class XmlAttrPrinter : public visitors::BaseVisitor } private: - RenderContext* m_context; - bool m_isFirstLevel; + RenderContext* m_context{}; + bool m_isFirstLevel{}; }; XmlAttrFilter::XmlAttrFilter(FilterParams) {} diff --git a/src/statements.cpp b/src/statements.cpp index 25bc11cc..8caa2a9b 100644 --- a/src/statements.cpp +++ b/src/statements.cpp @@ -48,7 +48,7 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende auto loopItems = ConvertToList(loopVal, isConverted, false); ListAdapter filteredList; ListAdapter indexedList; - ListAccessorEnumeratorPtr enumerator; + nonstd::optional enumerator; size_t itemIdx = 0; if (!isConverted) { @@ -78,13 +78,13 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende InternalValueList items; do { - items.push_back(enumerator->GetCurrent()); - } while (enumerator->MoveNext()); + items.push_back((*enumerator)->GetCurrent()); + } while ((*enumerator)->MoveNext()); listSize = itemIdx + items.size() + 1; indexedList = ListAdapter::CreateAdapter(std::move(items)); enumerator = indexedList.GetEnumerator(); - isLast = !enumerator->MoveNext(); + isLast = !(*enumerator)->MoveNext(); }; if (listSize) @@ -101,7 +101,7 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende }); } bool loopRendered = false; - isLast = !enumerator->MoveNext(); + isLast = !(*enumerator)->MoveNext(); InternalValue prevValue; InternalValue curValue; InternalValue nextValue; @@ -115,16 +115,18 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende loopVar["previtem"s] = prevValue; } else - curValue = enumerator->GetCurrent(); + curValue = (*enumerator)->GetCurrent(); - isLast = !enumerator->MoveNext(); + isLast = !(*enumerator)->MoveNext(); if (!isLast) { - nextValue = enumerator->GetCurrent(); + nextValue = (*enumerator)->GetCurrent(); loopVar["nextitem"s] = nextValue; } else + { loopVar.erase("nextitem"s); + } loopRendered = true; loopVar["index"s] = static_cast(itemIdx + 1); @@ -150,7 +152,9 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende } } else + { context[m_vars[0]] = curValue; + } values.EnterScope(); m_mainBody->Render(os, values); @@ -165,10 +169,13 @@ void ForStatement::RenderLoop(const InternalValue& loopVal, OutStream& os, Rende ListAdapter ForStatement::CreateFilteredAdapter(const ListAdapter& loopItems, RenderContext& values) const { - return ListAdapter::CreateAdapter([e = loopItems.GetEnumerator(), this, &values]() mutable { + return ListAdapter::CreateAdapter([eo = loopItems.GetEnumerator(), this, &values]() mutable { using ResultType = nonstd::optional; auto& tempContext = values.EnterScope(); + if (!eo.has_value()) + return ResultType(); + auto& e = *eo; for (bool finish = !e->MoveNext(); !finish; finish = !e->MoveNext()) { auto curValue = e->GetCurrent(); diff --git a/src/template_impl.h b/src/template_impl.h index 8f4ad3a6..06884520 100644 --- a/src/template_impl.h +++ b/src/template_impl.h @@ -2,59 +2,34 @@ #define JINJA2CPP_SRC_TEMPLATE_IMPL_H #include "internal_value.h" -#include "jinja2cpp/binding/rapid_json.h" #include "jinja2cpp/template_env.h" #include "jinja2cpp/value.h" #include "renderer.h" #include "template_parser.h" #include "value_visitors.h" +#ifdef JINJA2CPP_WITH_JSON_BINDINGS_BOOST +#include "binding/boost_json_parser.h" +#include "binding/boost_json_serializer.h" +#include "jinja2cpp/binding/boost_json.h" +#elif JINJA2CPP_WITH_JSON_BINDINGS_NLOHMANN +#include "binding/nlohmann_json_parser.h" +#include "jinja2cpp/binding/nlohmann_json.h" +#else +#include "binding/rapid_json_parser.h" +#include "jinja2cpp/binding/rapid_json.h" +#endif + +#include +#include #include #include #include -#include #include namespace jinja2 { -namespace detail -{ -template -struct RapidJsonEncodingType; - -template<> -struct RapidJsonEncodingType<1> -{ - using type = rapidjson::UTF8; -}; - -#ifdef BOOST_ENDIAN_BIG_BYTE -template<> -struct RapidJsonEncodingType<2> -{ - using type = rapidjson::UTF16BE; -}; - -template<> -struct RapidJsonEncodingType<4> -{ - using type = rapidjson::UTF32BE; -}; -#else -template<> -struct RapidJsonEncodingType<2> -{ - using type = rapidjson::UTF16LE; -}; - -template<> -struct RapidJsonEncodingType<4> -{ - using type = rapidjson::UTF32LE; -}; -#endif -} // namespace detail extern void SetupGlobals(InternalValueMap& globalParams); @@ -64,7 +39,6 @@ class ITemplateImpl virtual ~ITemplateImpl() = default; }; - template struct TemplateLoader; @@ -350,18 +324,16 @@ class TemplateImpl : public ITemplateImpl if (m_metadataInfo.metadataType == "json") { - m_metadataJson = JsonDocumentType(); - rapidjson::ParseResult res = m_metadataJson.value().Parse(metadataString.data(), metadataString.size()); - if (!res) + auto result = Parse(metadataString, m_metadataJson); + if (!result) { typename ErrorInfoTpl::Data errorData; errorData.code = ErrorCode::MetadataParseError; errorData.srcLoc = m_metadataInfo.location; - std::string jsonError = rapidjson::GetParseError_En(res.Code()); - errorData.extraParams.push_back(Value(std::move(jsonError))); + errorData.extraParams.push_back(Value(result.error())); return nonstd::make_unexpected(ErrorInfoTpl(errorData)); } - m_metadata = std::move(nonstd::get(Reflect(m_metadataJson.value()).data())); + m_metadata = std::move(nonstd::get(result.value().data())); return m_metadata.value(); } return GenericMap(); @@ -384,8 +356,9 @@ class TemplateImpl : public ITemplateImpl return false; if (m_metadata != other.m_metadata) return false; - if (m_metadataJson != other.m_metadataJson) - return false; + // m_metadataJson - only for persistence purposes + //if (m_metadataJson != other.m_metadataJson) + // return false; if (m_metadataInfo != other.m_metadataInfo) return false; return true; @@ -467,18 +440,16 @@ class TemplateImpl : public ITemplateImpl } private: - ThisType* m_host; + ThisType* m_host{}; }; private: - using JsonDocumentType = rapidjson::GenericDocument::type>; - TemplateEnv* m_env{}; Settings m_settings; std::basic_string m_template; std::string m_templateName; RendererPtr m_renderer; mutable nonstd::optional m_metadata; - mutable nonstd::optional m_metadataJson; + mutable boost::anys::unique_any m_metadataJson; MetadataInfo m_metadataInfo; }; diff --git a/src/template_parser.cpp b/src/template_parser.cpp index bd090393..2e154e95 100644 --- a/src/template_parser.cpp +++ b/src/template_parser.cpp @@ -100,16 +100,16 @@ StatementsParser::ParseResult StatementsParser::Parse(LexScanner& lexer, Stateme struct ErrorTokenConverter { const Token& baseTok; - + explicit ErrorTokenConverter(const Token& t) : baseTok(t) {} - + Token operator()(const Token& tok) const { return tok; } - + template Token operator()(T tokType) const { @@ -125,7 +125,7 @@ template auto MakeParseErrorTL(ErrorCode code, const Token& baseTok, Args ... expectedTokens) { ErrorTokenConverter tokCvt(baseTok); - + return MakeParseError(code, baseTok, {tokCvt(expectedTokens)...}); } @@ -409,7 +409,7 @@ StatementsParser::ParseResult StatementsParser::ParseBlock(LexScanner& lexer, St if (nextTok != Token::Eof) return MakeParseErrorTL(ErrorCode::ExpectedToken, nextTok, Token::Scoped); } - + blockRenderer = std::make_shared(blockName, isScoped); } @@ -433,7 +433,7 @@ StatementsParser::ParseResult StatementsParser::ParseEndBlock(LexScanner& lexer, tok3.type = Token::Eof; return MakeParseError(ErrorCode::ExpectedToken, nextTok, {tok2, tok3}); } - + if (nextTok == Token::Identifier) lexer.EatToken(); @@ -533,7 +533,6 @@ nonstd::expected StatementsParser::ParseMacroParams(Lex return std::move(items); ExpressionParser exprParser(m_settings); - do { Token name = lexer.NextToken(); @@ -676,7 +675,7 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, isIgnoreMissing = true; else return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Missing); - + hasIgnoreMissing = true; nextTok = lexer.PeekNextToken(); } @@ -689,8 +688,8 @@ StatementsParser::ParseResult StatementsParser::ParseInclude(LexScanner& lexer, isWithContext = kw == Keyword::With; if (!lexer.EatIfEqual(Keyword::Context)) return MakeParseErrorTL(ErrorCode::ExpectedToken, lexer.PeekNextToken(), Token::Context); - - nextTok = lexer.PeekNextToken(); + + nextTok = lexer.PeekNextToken(); hasContextControl = true; } @@ -945,7 +944,7 @@ StatementsParser::ParseResult StatementsParser::ParseFilter(LexScanner& lexer, S StatementInfo::FilterStatement, stmtTok); statementInfo.renderer = std::move(renderer); statementsInfo.push_back(std::move(statementInfo)); - + return {}; } diff --git a/src/value_visitors.h b/src/value_visitors.h index 193680c5..0a3a4427 100644 --- a/src/value_visitors.h +++ b/src/value_visitors.h @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -313,6 +312,21 @@ struct InputValueConvertor return this->operator()(*val); } + result_t operator()(bool val) const + { + return result_t(InternalValue(val)); + } + + result_t operator()(int64_t val) const + { + return result_t(InternalValue(val)); + } + + result_t operator()(double val) const + { + return result_t(InternalValue(val)); + } + template result_t operator()(const T& val) const { diff --git a/test/basic_tests.cpp b/test/basic_tests.cpp index e9e53ddf..ab985edb 100644 --- a/test/basic_tests.cpp +++ b/test/basic_tests.cpp @@ -655,13 +655,13 @@ TEST(BasicTests, EnvTestPreservesGlobalVar) std::string result1; { jinja2::Template tpl(&tplEnv); - tpl.Load("Hello {{ global_var }} {{ global_fn() }}!!!"); + ASSERT_TRUE(tpl.Load("Hello {{ global_var }} {{ global_fn() }}!!!")); result1 = tpl.RenderAsString(jinja2::ValuesMap{}).value(); } std::string result2; { jinja2::Template tpl(&tplEnv); - tpl.Load("Hello {{ global_var }} {{ global_fn() }}!!!"); + ASSERT_TRUE(tpl.Load("Hello {{ global_var }} {{ global_fn() }}!!!")); result2 = tpl.RenderAsString(jinja2::ValuesMap{}).value(); } ASSERT_EQ(result1, result2); diff --git a/test/errors_test.cpp b/test/errors_test.cpp index bcb137b1..c56b7e93 100644 --- a/test/errors_test.cpp +++ b/test/errors_test.cpp @@ -40,8 +40,7 @@ TEST_F(TemplateEnvFixture, EnvironmentAbsentErrorsTest_Wide) TemplateW tpl1; auto parseResult = tpl1.Load(L"{% extends 'module' %}"); ASSERT_FALSE(parseResult.has_value()); - - EXPECT_EQ(L"noname.j2tpl:1:4: error: Template environment doesn't set\n{% extends 'module' %}\n---^-------", ErrorToString(parseResult.error())); + EXPECT_EQ(std::wstring(L"noname.j2tpl:1:4: error: Template environment doesn't set\n{% extends 'module' %}\n---^-------"), ErrorToString(parseResult.error())); parseResult = tpl1.Load(L"{% include 'module' %}"); ASSERT_FALSE(parseResult.has_value()); @@ -68,7 +67,7 @@ TEST_F(TemplateEnvFixture, RenderErrorsTest) EXPECT_EQ(":1:1: error: Template not parsed\n", ErrorToString(renderResult.error())); Template tpl2; - tpl2.Load(R"({{ foo() }})"); + ASSERT_TRUE(tpl2.Load(R"({{ foo() }})")); renderResult = tpl2.RenderAsString({{"foo", MakeCallable([]() -> Value {throw std::runtime_error("Bang!"); })}}); ASSERT_FALSE(renderResult.has_value()); @@ -94,7 +93,7 @@ TEST_F(TemplateEnvFixture, RenderErrorsTest_Wide) EXPECT_EQ(L":1:1: error: Template not parsed\n", ErrorToString(renderResult.error())); TemplateW tpl2; - tpl2.Load(LR"({{ foo() }})"); + ASSERT_TRUE(tpl2.Load(LR"({{ foo() }})")); renderResult = tpl2.RenderAsString({ {"foo", MakeCallable([]() -> Value {throw std::runtime_error("Bang!"); })} }); ASSERT_FALSE(renderResult.has_value()); diff --git a/test/filters_test.cpp b/test/filters_test.cpp index aa67bacb..e6c04809 100644 --- a/test/filters_test.cpp +++ b/test/filters_test.cpp @@ -238,10 +238,10 @@ INSTANTIATE_TEST_SUITE_P(Map, ListIteratorTest, ::testing::Values( InputOutputPair{"reflectedList | map(attribute='intValue', default='99')", "0, 1, 2, 3, 4, 5, 6, 7, 8, 9"}, InputOutputPair{"reflectedList | map(attribute='intEvenValue')", "0, , 2, , 4, , 6, , 8, "}, - InputOutputPair{"reflectedList | map(attribute='intEvenValue', default='99')", - "0, 99, 2, 99, 4, 99, 6, 99, 8, 99"}, + InputOutputPair{"reflectedList | map(attribute='intEvenValue', default='99')", + "0, 99, 2, 99, 4, 99, 6, 99, 8, 99"}, InputOutputPair{"reflectedList | map(attribute='nonexistent')", ", , , , , , , , , "}, - InputOutputPair{"reflectedList | map(attribute='nonexistent', default='99')", + InputOutputPair{"reflectedList | map(attribute='nonexistent', default='99')", "99, 99, 99, 99, 99, 99, 99, 99, 99, 99"}, InputOutputPair{"[[0, 1], [1, 2], [2, 3], [3, 4]] | map('first')", "0, 1, 2, 3"}, InputOutputPair{"[['str1', 'Str2'], ['str2', 'Str3'], ['str3', 'Str4'], ['str4', 'Str5']] | map('min')", @@ -586,7 +586,7 @@ struct XmlAttr : ::testing::Test using String = std::basic_string; using Regex = std::basic_regex; using RegexTokenIterator = std::regex_token_iterator; - + AttributeSet result; const Regex pattern(ConvertString(std::string("(\\S+=[\"].*?[\"])"))); std::copy(RegexTokenIterator(attributeString.begin(), attributeString.end(), pattern, 0), @@ -621,14 +621,14 @@ struct XmlAttr : ::testing::Test TemplateType tpl; if (!tpl.Load(source)) return false; - + return tpl.RenderAsString(params).has_value(); } void PerformNegativeTest(const std::string& source, const jinja2::ValuesMap& params = {}) { EXPECT_FALSE(ParseTemplate