From 72ef82107dce180b05ee7ceed95fd10b83a95643 Mon Sep 17 00:00:00 2001 From: Evgenii Zhemchugov Date: Wed, 30 Apr 2025 11:54:50 +0100 Subject: [PATCH 1/4] Provide json_encode for const char* --- src/Store/Json.cpp | 14 +++++++++----- src/Store/Json.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Store/Json.cpp b/src/Store/Json.cpp index 1eaa001..db405b6 100644 --- a/src/Store/Json.cpp +++ b/src/Store/Json.cpp @@ -2,14 +2,18 @@ namespace ToolFramework { -bool json_encode(std::ostream& output, const std::string& datum) { +bool json_encode(std::ostream& output, const char* datum) { output << '"'; - for (char c : datum) { - if (c == '"' || c == '\\') output << '\\'; - output << c; + while (*datum) { + if (*datum == '"' || *datum == '\\') output << '\\'; + output << *datum; }; output << '"'; return true; -} +}; + +bool json_encode(std::ostream& output, const std::string& datum) { + return json_encode(output, datum.c_str()); +}; } diff --git a/src/Store/Json.h b/src/Store/Json.h index 9118d16..9c7c794 100644 --- a/src/Store/Json.h +++ b/src/Store/Json.h @@ -17,6 +17,7 @@ json_encode(std::ostream& output, T datum) { return true; } +bool json_encode(std::ostream& output, const char* datum); bool json_encode(std::ostream& output, const std::string& datum); template From cc184ae5a8f9fd22b1debf4d1bf644870cee40fd Mon Sep 17 00:00:00 2001 From: Evgenii Zhemchugov Date: Wed, 30 Apr 2025 11:57:31 +0100 Subject: [PATCH 2/4] json_encode for std::vector and std::map: propagate errors --- src/Store/Json.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Store/Json.h b/src/Store/Json.h index 9c7c794..a96d420 100644 --- a/src/Store/Json.h +++ b/src/Store/Json.h @@ -29,7 +29,7 @@ bool json_encode(std::ostream& output, const std::vector& data) { output << ','; else comma = true; - json_encode(output, datum); + if (!json_encode(output, datum)) return false; }; output << ']'; return true; @@ -44,9 +44,9 @@ bool json_encode(std::ostream& output, const std::map& data) { output << ','; else comma = true; - json_encode(output, datum.first); + if (!json_encode(output, datum.first)) return false; output << ':'; - json_encode(output, datum.second); + if (!json_encode(output, datum.second)) return false; }; output << '}'; return true; From fc8a27b572ca70cf190adf88bbaf5378fa74f30c Mon Sep 17 00:00:00 2001 From: Evgenii Zhemchugov Date: Wed, 30 Apr 2025 11:58:08 +0100 Subject: [PATCH 3/4] Implement json_encode for pointers and std::array Testing revealed difficulties regarding extending json_encode with user-defined types. In the following chain of calls: struct S { ... }; json_encode(std::string, std::vector>); // 1 json_encode(std::ostream, std::vector>); // 2 json_encode(std::ostream, std::array*, ...); // 3 json_encode(std::ostream, std::array); // 4 json_encode(std::ostream, S*); // 5 json_encode(std::ostream, S); // 6, implemented in the user code the compiler might only be able to find the user function if it was called with unqualified names and argument-dependent lookup took action. Also, without forward declarations provided by this patch the compiler could not reach step 4 since json_encode for array has been declared after json_encode for pointers. This suggests possible problems with user and other STL containers. See https://stackoverflow.com/questions/28187170/c-enforce-second-pass-name-lookup-in-template-function https://akrzemi1.wordpress.com/2015/11/19/overload-resolution/ for the discussion. One possible solution is to declare template void json_encode(std::ostream&, const T&); and use template specializations instead of overloaded functions, but it needs special casing to exclude arithmetic types implemented by a different template --- and possibly it may clash in a similar way with other user defined types. --- src/Store/Json.h | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Store/Json.h b/src/Store/Json.h index a96d420..42171d1 100644 --- a/src/Store/Json.h +++ b/src/Store/Json.h @@ -10,6 +10,15 @@ namespace ToolFramework { +template +bool json_encode(std::ostream&, const std::array&); + +template +bool json_encode(std::ostream&, const std::vector&); + +template +bool json_encode(std::ostream&, const std::map&); + template typename std::enable_if::value, bool>::type json_encode(std::ostream& output, T datum) { @@ -21,19 +30,28 @@ bool json_encode(std::ostream& output, const char* datum); bool json_encode(std::ostream& output, const std::string& datum); template -bool json_encode(std::ostream& output, const std::vector& data) { +bool json_encode(std::ostream& output, const T* data, size_t size) { output << '['; bool comma = false; - for (const T& datum : data) { + for (size_t i = 0; i < size; ++i) { if (comma) output << ','; - else - comma = true; - if (!json_encode(output, datum)) return false; + comma = true; + if (!json_encode(output, data[i])) return false; }; output << ']'; return true; -} +}; + +template +bool json_encode(std::ostream& output, const std::array& array) { + return json_encode(output, array.data(), array.size()); +}; + +template +bool json_encode(std::ostream& output, const std::vector& vector) { + return json_encode(output, vector.data(), vector.size()); +}; template bool json_encode(std::ostream& output, const std::map& data) { From c82fab40df32791f04efb8091aaf46e84c569bc3 Mon Sep 17 00:00:00 2001 From: Evgenii Zhemchugov Date: Wed, 30 Apr 2025 12:16:15 +0100 Subject: [PATCH 4/4] Implement json_encode_object --- src/Store/Json.h | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/src/Store/Json.h b/src/Store/Json.h index 42171d1..4e71727 100644 --- a/src/Store/Json.h +++ b/src/Store/Json.h @@ -78,6 +78,54 @@ bool json_encode(std::string& output, T data) { return true; } +namespace json_internal { + + inline bool json_encode_object_slots(std::ostream& output, bool& comma) { + return true; + }; + + template + bool json_encode_object_slots( + std::ostream& output, + bool& comma, + const char* name, + const Slot& slot, + Rest... rest + ) { + if (comma) output << ','; + comma = true; + output << '"' << name << '"' << ':'; + if (!json_encode(output, slot)) return false; + return json_encode_object_slots(output, comma, rest...); + }; + + template + bool json_encode_object_slots( + std::ostream& output, + bool& comma, + const std::string& name, + const Slot& slot, + Rest... rest + ) { + return json_encode_object_slots(output, comma, name.c_str(), slot, rest...); + }; + +}; // json_internal + + +// A helper function to write fixed-size objects +// Example: call `json_encode_object(output, "x", 42, "a", false)` +// to produce `{"x":42,"a":false}` +template +bool json_encode_object(std::ostream& output, Args... args) { + output << '{'; + bool comma = false; + if (!json_internal::json_encode_object_slots(output, comma, args...)) + return false; + output << '}'; + return true; } +}; // ToolFramework + #endif