From e03a06b332f91b9f06a887ee343df91a73295d8e Mon Sep 17 00:00:00 2001 From: "marek.piotrowski" Date: Thu, 20 Jul 2023 11:44:44 +0200 Subject: [PATCH] Clean up, move code around --- example/main.cpp | 14 +- include/nlohmann/detail/macro_scope.hpp | 30 --- .../nlohmann/detail/macro_scope_annotated.hpp | 27 +++ include/nlohmann/detail/output/serializer.hpp | 208 +++++++++++++++++- include/nlohmann/json.hpp | 13 +- 5 files changed, 244 insertions(+), 48 deletions(-) create mode 100644 include/nlohmann/detail/macro_scope_annotated.hpp diff --git a/example/main.cpp b/example/main.cpp index 166107f85..98218aa2e 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -15,8 +15,8 @@ public: ExampleClass() = default; NLOHMANN_DEFINE_TYPE_INTRUSIVE_ANNOTATED(ExampleClass, property1, "comment1", - property2, "comment2", - property3, "comment3"); + property2, "multiline\ncomment2", + property5, "comment5"); }; class AnotherExampleClass { @@ -30,8 +30,8 @@ public: AnotherExampleClass() = default; NLOHMANN_DEFINE_TYPE_INTRUSIVE_ANNOTATED(AnotherExampleClass, property1, "comment11", - property2, "comment22", - property3, "comment33"); + property2, "multiline\ncomment22", + property3, "comment33"); }; int main() { @@ -40,9 +40,9 @@ int main() { AnotherExampleClass aec; nlohmann::json j = ec; - std::cout << j.dump_annotated() << std::endl; + std::cout << j.dump_annotated(4) << std::endl; - nlohmann::json j2 = aec; - std::cout << j2.dump_annotated() << std::endl; + // nlohmann::json j2 = aec; + // std::cout << j2.dump_annotated() << std::endl; return 0; } \ No newline at end of file diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 54f9190b6..2870a4f1d 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -382,24 +382,6 @@ #define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) #define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; - - -#define NLOHMANN_JSON_ANNOTATED_EXPAND( x ) x -#define NLOHMANN_JSON_ANNOTATED_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, NAME,...) NAME -#define NLOHMANN_JSON_ANNOTATED_PASTE(...) NLOHMANN_JSON_ANNOTATED_EXPAND(NLOHMANN_JSON_ANNOTATED_GET_MACRO(__VA_ARGS__, \ - NLOHMANN_JSON_ANNOTATED_PASTE7, \ - NLOHMANN_JSON_ANNOTATED_PASTE5, \ - NLOHMANN_JSON_ANNOTATED_PASTE2, \ - NLOHMANN_JSON_ANNOTATED_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) func(v1, w1) -// #define NLOHMANN_JSON_ANNOTATED_PASTE3(func, v1, w1, v2, w2) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v2, w2) -// #define NLOHMANN_JSON_ANNOTATED_PASTE4(func, v1, w1, v2, w2, v3, w3) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE3(func, v2, w2, v3, w3) -#define NLOHMANN_JSON_ANNOTATED_PASTE5(func, v1, w1, v2, w2) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v2, w2) -#define NLOHMANN_JSON_ANNOTATED_PASTE7(func, v1, w1, v2, w2, v3, w3) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE5(func, v2, w2, v3, w3) - -#define NLOHMANN_JSON_ANNOTATED_TO(v1, w1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -// #define NLOHMANN_EXPAND_ANNOTATION(v1, w1) static constexpr char annotation_##v1[] = w1; - #define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); #define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1); @@ -412,18 +394,6 @@ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } -#define TERNARY_EXPAND1(v1, w1) (property == #v1 ? w1 : "") -#define TERNARY_EXPAND2(v1, w1, v2, w2) (property == #v2 ? w2 : TERNARY_EXPAND1(v1, w1)) -#define TERNARY_EXPAND3(v1, w1, v2, w2, v3, w3) (property == #v3 ? w3 : TERNARY_EXPAND2(v1, w1, v2, w2)) -#define TERNARY_NOT_ALLOWED - -#define GET_TERNARY_EXPAND_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME -#define TERNARY_EXPAND(...) GET_TERNARY_EXPAND_MACRO(__VA_ARGS__, TERNARY_EXPAND3, TERNARY_NOT_ALLOWED, TERNARY_EXPAND2, TERNARY_NOT_ALLOWED, TERNARY_EXPAND1)(__VA_ARGS__) - -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ANNOTATED(Type, ...) \ - friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_ANNOTATED_EXPAND(NLOHMANN_JSON_ANNOTATED_PASTE(NLOHMANN_JSON_ANNOTATED_TO, __VA_ARGS__)) } \ - static std::string get_annotation(const std::string& property) { return TERNARY_EXPAND(__VA_ARGS__); } - #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } diff --git a/include/nlohmann/detail/macro_scope_annotated.hpp b/include/nlohmann/detail/macro_scope_annotated.hpp new file mode 100644 index 000000000..0611af2a5 --- /dev/null +++ b/include/nlohmann/detail/macro_scope_annotated.hpp @@ -0,0 +1,27 @@ +#include + +#define NLOHMANN_JSON_ANNOTATED_TO(v1, w1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; + +#define NLOHMANN_JSON_ANNOTATED_EXPAND( x ) x +#define NLOHMANN_JSON_ANNOTATED_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, NAME,...) NAME +#define NLOHMANN_JSON_ANNOTATED_PASTE(...) NLOHMANN_JSON_ANNOTATED_EXPAND(NLOHMANN_JSON_ANNOTATED_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_ANNOTATED_PASTE7, \ + NLOHMANN_JSON_ANNOTATED_PASTE5, \ + NLOHMANN_JSON_ANNOTATED_PASTE2, \ + NLOHMANN_JSON_ANNOTATED_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) func(v1, w1) +#define NLOHMANN_JSON_ANNOTATED_PASTE5(func, v1, w1, v2, w2) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v2, w2) +#define NLOHMANN_JSON_ANNOTATED_PASTE7(func, v1, w1, v2, w2, v3, w3) NLOHMANN_JSON_ANNOTATED_PASTE2(func, v1, w1) NLOHMANN_JSON_ANNOTATED_PASTE5(func, v2, w2, v3, w3) + + +#define TERNARY_EXPAND1(v1, w1) (property == #v1 ? w1 : "") +#define TERNARY_EXPAND2(v1, w1, v2, w2) (property == #v2 ? w2 : TERNARY_EXPAND1(v1, w1)) +#define TERNARY_EXPAND3(v1, w1, v2, w2, v3, w3) (property == #v3 ? w3 : TERNARY_EXPAND2(v1, w1, v2, w2)) +#define TERNARY_NOT_ALLOWED static_assert(false, "Annotated macro requires even number of arguments where each property is accompanied by a string comment.") + +#define GET_TERNARY_EXPAND_MACRO(_1,_2,_3,_4,_5,_6,NAME,...) NAME +#define TERNARY_EXPAND(...) GET_TERNARY_EXPAND_MACRO(__VA_ARGS__, TERNARY_EXPAND3, TERNARY_NOT_ALLOWED, TERNARY_EXPAND2, TERNARY_NOT_ALLOWED, TERNARY_EXPAND1)(__VA_ARGS__) + +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ANNOTATED(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_ANNOTATED_EXPAND(NLOHMANN_JSON_ANNOTATED_PASTE(NLOHMANN_JSON_ANNOTATED_TO, __VA_ARGS__)) } \ + static std::string get_annotation(const std::string& property) { return TERNARY_EXPAND(__VA_ARGS__); } \ No newline at end of file diff --git a/include/nlohmann/detail/output/serializer.hpp b/include/nlohmann/detail/output/serializer.hpp index a05edb12a..331fb1a8f 100644 --- a/include/nlohmann/detail/output/serializer.hpp +++ b/include/nlohmann/detail/output/serializer.hpp @@ -21,6 +21,7 @@ #include // setfill, setw #include // is_same #include // move +#include #include #include @@ -31,6 +32,7 @@ #include #include + NLOHMANN_JSON_NAMESPACE_BEGIN namespace detail { @@ -375,11 +377,195 @@ class serializer template void dump_annotated(const BasicJsonType& val, - const bool pretty_print, const bool ensure_ascii, const unsigned int indent_step, const unsigned int current_indent = 0) { - o->write_characters(UnderlyingType::get_annotation("property3").c_str(), UnderlyingType::get_annotation("property1").size()); + // o->write_characters(UnderlyingType::get_annotation("property3").c_str(), UnderlyingType::get_annotation("property1").size()); + switch (val.m_data.m_type) + { + case value_t::object: + { + if (val.m_data.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_data.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_data.m_value.object->size() - 1; ++cnt, ++i) + { + write_annotation_if_available(i->first, indent_string, new_indent); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_data.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_data.m_value.object->cend()); + write_annotation_if_available(i->first, indent_string, new_indent); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + + return; + } + + case value_t::array: + { + if (val.m_data.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_data.m_value.array->cbegin(); + i != val.m_data.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_data.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_data.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_data.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_data.m_value.binary->empty()) + { + for (auto i = val.m_data.m_value.binary->cbegin(); + i != val.m_data.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_data.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_data.m_value.binary->has_subtype()) + { + dump_integer(val.m_data.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + return; + } + + case value_t::boolean: + { + if (val.m_data.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_data.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_data.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_data.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } } JSON_PRIVATE_UNLESS_TESTED: /*! @@ -966,6 +1152,24 @@ class serializer return static_cast(-(x + 1)) + 1; } + template + void write_annotation_if_available(const std::string& property, const std::string& indent_string, unsigned int new_indent) + { + // TODO potentially add escaping option + const auto annotation = UnderlyingType::get_annotation(property); + if(annotation != "") { + std::stringstream ss{annotation}; + for (std::string line; std::getline(ss, line, '\n');) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_characters("/* ", 3); + o->write_characters(line.c_str(), line.size()); + o->write_characters(" */", 3); + o->write_characters("\n", 1); + } + } + } + private: /// the output of the serializer output_adapter_t o = nullptr; diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 4ccdad5a6..f7ea7f0be 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -60,6 +60,7 @@ #include #include #include +#include #if defined(JSON_HAS_CPP_17) #include @@ -1290,7 +1291,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec } template - string_t dump_annotated(const int indent = -1, + string_t dump_annotated(const int indent = 4, const char indent_char = ' ', const bool ensure_ascii = false, const error_handler_t error_handler = error_handler_t::strict) const @@ -1298,14 +1299,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec string_t result; serializer s(detail::output_adapter(result), indent_char, error_handler); - if (indent >= 0) - { - s.template dump_annotated(*this, true, ensure_ascii, static_cast(indent)); - } - else - { - s.template dump_annotated(*this, false, ensure_ascii, 0); - } + JSON_ASSERT(indent >= 0); + s.template dump_annotated(*this, ensure_ascii, static_cast(indent)); return result; }