From 87fb892cf61de9284af4878504a0f0c655a3a13c Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Wed, 27 Mar 2024 18:39:24 +0100 Subject: [PATCH 1/2] Fix json implementations with unstable pointers (`INJA_DATA_TYPE=nlohmann::ordered_json`) * Closes #289 --- include/inja/renderer.hpp | 45 ++++++++++++++++++++++++------------ single_include/inja/inja.hpp | 45 ++++++++++++++++++++++++------------ 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/include/inja/renderer.hpp b/include/inja/renderer.hpp index ba784c08..4d8d0c21 100644 --- a/include/inja/renderer.hpp +++ b/include/inja/renderer.hpp @@ -34,7 +34,10 @@ class Renderer : public NodeVisitor { std::ostream* output_stream; json additional_data; - json* current_loop_data = &additional_data["loop"]; + + json* get_current_loop_data() { + return &additional_data["loop"]; + } std::vector> data_tmp_stack; std::stack data_eval_stack; @@ -485,17 +488,24 @@ class Renderer : public NodeVisitor { throw_renderer_error("object must be an array", node); } - if (!current_loop_data->empty()) { - auto tmp = *current_loop_data; // Because of clang-3 - (*current_loop_data)["parent"] = std::move(tmp); + { + json* const current_loop_data = get_current_loop_data(); + if (!current_loop_data->empty()) { + auto tmp = *current_loop_data; // Because of clang-3 + (*current_loop_data)["parent"] = std::move(tmp); + } + + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); } size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { additional_data[static_cast(node.value)] = *it; + json* const current_loop_data = get_current_loop_data(); + (*current_loop_data)["index"] = index; (*current_loop_data)["index1"] = index + 1; if (index == 1) { @@ -510,11 +520,11 @@ class Renderer : public NodeVisitor { } additional_data[static_cast(node.value)].clear(); + + json* const current_loop_data = get_current_loop_data(); if (!(*current_loop_data)["parent"].empty()) { const auto tmp = (*current_loop_data)["parent"]; *current_loop_data = std::move(tmp); - } else { - current_loop_data = &additional_data["loop"]; } } @@ -524,17 +534,23 @@ class Renderer : public NodeVisitor { throw_renderer_error("object must be an object", node); } - if (!current_loop_data->empty()) { - (*current_loop_data)["parent"] = std::move(*current_loop_data); + { + json* const current_loop_data = get_current_loop_data(); + if (!current_loop_data->empty()) { + (*current_loop_data)["parent"] = std::move(*current_loop_data); + } + + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); } size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { additional_data[static_cast(node.key)] = it.key(); additional_data[static_cast(node.value)] = it.value(); + json* const current_loop_data = get_current_loop_data(); (*current_loop_data)["index"] = index; (*current_loop_data)["index1"] = index + 1; if (index == 1) { @@ -550,10 +566,10 @@ class Renderer : public NodeVisitor { additional_data[static_cast(node.key)].clear(); additional_data[static_cast(node.value)].clear(); + + json* const current_loop_data = get_current_loop_data(); if (!(*current_loop_data)["parent"].empty()) { *current_loop_data = std::move((*current_loop_data)["parent"]); - } else { - current_loop_data = &additional_data["loop"]; } } @@ -618,7 +634,6 @@ class Renderer : public NodeVisitor { data_input = &data; if (loop_data) { additional_data = *loop_data; - current_loop_data = &additional_data["loop"]; } template_stack.emplace_back(current_template); diff --git a/single_include/inja/inja.hpp b/single_include/inja/inja.hpp index 3535da0b..f7423a6b 100644 --- a/single_include/inja/inja.hpp +++ b/single_include/inja/inja.hpp @@ -2103,7 +2103,10 @@ class Renderer : public NodeVisitor { std::ostream* output_stream; json additional_data; - json* current_loop_data = &additional_data["loop"]; + + json* get_current_loop_data() { + return &additional_data["loop"]; + } std::vector> data_tmp_stack; std::stack data_eval_stack; @@ -2554,17 +2557,24 @@ class Renderer : public NodeVisitor { throw_renderer_error("object must be an array", node); } - if (!current_loop_data->empty()) { - auto tmp = *current_loop_data; // Because of clang-3 - (*current_loop_data)["parent"] = std::move(tmp); + { + json* const current_loop_data = get_current_loop_data(); + if (!current_loop_data->empty()) { + auto tmp = *current_loop_data; // Because of clang-3 + (*current_loop_data)["parent"] = std::move(tmp); + } + + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); } size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { additional_data[static_cast(node.value)] = *it; + json* const current_loop_data = get_current_loop_data(); + (*current_loop_data)["index"] = index; (*current_loop_data)["index1"] = index + 1; if (index == 1) { @@ -2579,11 +2589,11 @@ class Renderer : public NodeVisitor { } additional_data[static_cast(node.value)].clear(); + + json* const current_loop_data = get_current_loop_data(); if (!(*current_loop_data)["parent"].empty()) { const auto tmp = (*current_loop_data)["parent"]; *current_loop_data = std::move(tmp); - } else { - current_loop_data = &additional_data["loop"]; } } @@ -2593,17 +2603,23 @@ class Renderer : public NodeVisitor { throw_renderer_error("object must be an object", node); } - if (!current_loop_data->empty()) { - (*current_loop_data)["parent"] = std::move(*current_loop_data); + { + json* const current_loop_data = get_current_loop_data(); + if (!current_loop_data->empty()) { + (*current_loop_data)["parent"] = std::move(*current_loop_data); + } + + (*current_loop_data)["is_first"] = true; + (*current_loop_data)["is_last"] = (result->size() <= 1); } size_t index = 0; - (*current_loop_data)["is_first"] = true; - (*current_loop_data)["is_last"] = (result->size() <= 1); + for (auto it = result->begin(); it != result->end(); ++it) { additional_data[static_cast(node.key)] = it.key(); additional_data[static_cast(node.value)] = it.value(); + json* const current_loop_data = get_current_loop_data(); (*current_loop_data)["index"] = index; (*current_loop_data)["index1"] = index + 1; if (index == 1) { @@ -2619,10 +2635,10 @@ class Renderer : public NodeVisitor { additional_data[static_cast(node.key)].clear(); additional_data[static_cast(node.value)].clear(); + + json* const current_loop_data = get_current_loop_data(); if (!(*current_loop_data)["parent"].empty()) { *current_loop_data = std::move((*current_loop_data)["parent"]); - } else { - current_loop_data = &additional_data["loop"]; } } @@ -2687,7 +2703,6 @@ class Renderer : public NodeVisitor { data_input = &data; if (loop_data) { additional_data = *loop_data; - current_loop_data = &additional_data["loop"]; } template_stack.emplace_back(current_template); From eb18275f7c351bd3ef17cc32ed9ae2d4ba1357f7 Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Wed, 27 Mar 2024 20:10:59 +0100 Subject: [PATCH 2/2] Add unit tests for stable insertion ordered json (`INJA_DATA_TYPE=nlohmann::ordered_json`) --- CMakeLists.txt | 6 ++++++ test/test-renderer.cpp | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28b2771c..2b4da7f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,12 @@ if(BUILD_TESTING AND INJA_BUILD_TESTS) target_link_libraries(single_inja_test PRIVATE single_inja) add_test(single_inja_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/single_inja_test) + + add_executable(single_inja_ordered_test test/test.cpp) + target_link_libraries(single_inja_ordered_test PRIVATE single_inja) + target_compile_definitions(single_inja_ordered_test PUBLIC INJA_DATA_TYPE=nlohmann::ordered_json) + + add_test(single_inja_ordered_test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/single_inja_ordered_test) endif() diff --git a/test/test-renderer.cpp b/test/test-renderer.cpp index 68268e7f..aa33c073 100644 --- a/test/test-renderer.cpp +++ b/test/test-renderer.cpp @@ -14,8 +14,8 @@ TEST_CASE("types") { data["is_sad"] = false; data["@name"] = "@name"; data["$name"] = "$name"; - data["relatives"]["mother"] = "Maria"; data["relatives"]["brother"] = "Chris"; + data["relatives"]["mother"] = "Maria"; data["relatives"]["sister"] = "Jenny"; data["vars"] = {2, 3, 4, 0, -1, -2, -3}; data["max_value"] = 18446744073709551615ull;