From 10e451b58d09aa4bfc704e82b450ab9bebd72f11 Mon Sep 17 00:00:00 2001 From: Florian Albrechtskirchinger Date: Sun, 3 Apr 2022 17:43:39 +0200 Subject: [PATCH] Add string concatenation function Add variadic concat() function for concatenating char *, char, and string types. --- include/nlohmann/detail/string_concat.hpp | 139 +++++++++++++++++++++ include/nlohmann/json.hpp | 1 + single_include/nlohmann/json.hpp | 143 ++++++++++++++++++++++ test/src/unit-convenience.cpp | 113 +++++++++++++++++ 4 files changed, 396 insertions(+) create mode 100644 include/nlohmann/detail/string_concat.hpp diff --git a/include/nlohmann/detail/string_concat.hpp b/include/nlohmann/detail/string_concat.hpp new file mode 100644 index 000000000..12f62289f --- /dev/null +++ b/include/nlohmann/detail/string_concat.hpp @@ -0,0 +1,139 @@ +#pragma once + +#include // strlen +#include // string +#include // forward + +#include +#include + +namespace nlohmann +{ +namespace detail +{ + +inline std::size_t concat_length() +{ + return 0; +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest); + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest); + +template +inline std::size_t concat_length(const char /*c*/, Args&& ... rest) +{ + return 1 + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest) +{ + // cppcheck-suppress ignoredReturnValue + return ::strlen(cstr) + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest) +{ + return str.size() + concat_length(std::forward(rest)...); +} + +template +inline void concat_into(OutStringType& /*out*/) +{} + +template +using string_can_append = decltype(std::declval().append(std::declval < Arg && > ())); + +template +using detect_string_can_append = is_detected; + +template +using string_can_append_op = decltype(std::declval() += std::declval < Arg && > ()); + +template +using detect_string_can_append_op = is_detected; + +template +using string_can_append_iter = decltype(std::declval().append(std::declval().begin(), std::declval().end())); + +template +using detect_string_can_append_iter = is_detected; + +template +using string_can_append_data = decltype(std::declval().append(std::declval().data(), std::declval().size())); + +template +using detect_string_can_append_data = is_detected; + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > = 0 > +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template::value, int> = 0> +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest) +{ + out.append(std::forward(arg)); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > > +inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest) +{ + out += std::forward(arg); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.begin(), arg.end()); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.data(), arg.size()); + concat_into(out, std::forward(rest)...); +} + +template +inline OutStringType concat(Args && ... args) +{ + OutStringType str; + str.reserve(concat_length(std::forward(args)...)); + concat_into(str, std::forward(args)...); + return str; +} + +} // namespace detail +} // namespace nlohmann diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 160ce9929..9bf19a374 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -83,6 +83,7 @@ SOFTWARE. #include #include #include +#include #include #include #include diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 8a80d00ab..c357d46e1 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -13187,6 +13187,149 @@ class json_ref // #include +// #include + + +#include // strlen +#include // string +#include // forward + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +inline std::size_t concat_length() +{ + return 0; +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest); + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest); + +template +inline std::size_t concat_length(const char /*c*/, Args&& ... rest) +{ + return 1 + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const char* cstr, Args&& ... rest) +{ + // cppcheck-suppress ignoredReturnValue + return ::strlen(cstr) + concat_length(std::forward(rest)...); +} + +template +inline std::size_t concat_length(const StringType& str, Args&& ... rest) +{ + return str.size() + concat_length(std::forward(rest)...); +} + +template +inline void concat_into(OutStringType& /*out*/) +{} + +template +using string_can_append = decltype(std::declval().append(std::declval < Arg && > ())); + +template +using detect_string_can_append = is_detected; + +template +using string_can_append_op = decltype(std::declval() += std::declval < Arg && > ()); + +template +using detect_string_can_append_op = is_detected; + +template +using string_can_append_iter = decltype(std::declval().append(std::declval().begin(), std::declval().end())); + +template +using detect_string_can_append_iter = is_detected; + +template +using string_can_append_data = decltype(std::declval().append(std::declval().data(), std::declval().size())); + +template +using detect_string_can_append_data = is_detected; + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > = 0 > +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > = 0 > +inline void concat_into(OutStringType& out, const Arg& arg, Args && ... rest); + +template::value, int> = 0> +inline void concat_into(OutStringType& out, Arg && arg, Args && ... rest) +{ + out.append(std::forward(arg)); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && detect_string_can_append_op::value, int > > +inline void concat_into(OutStringType& out, Arg&& arg, Args&& ... rest) +{ + out += std::forward(arg); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && detect_string_can_append_iter::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.begin(), arg.end()); + concat_into(out, std::forward(rest)...); +} + +template < typename OutStringType, typename Arg, typename... Args, + enable_if_t < !detect_string_can_append::value + && !detect_string_can_append_op::value + && !detect_string_can_append_iter::value + && detect_string_can_append_data::value, int > > +inline void concat_into(OutStringType& out, const Arg& arg, Args&& ... rest) +{ + out.append(arg.data(), arg.size()); + concat_into(out, std::forward(rest)...); +} + +template +inline OutStringType concat(Args && ... args) +{ + OutStringType str; + str.reserve(concat_length(std::forward(args)...)); + concat_into(str, std::forward(args)...); + return str; +} + +} // namespace detail +} // namespace nlohmann + // #include // #include diff --git a/test/src/unit-convenience.cpp b/test/src/unit-convenience.cpp index f2018bf3c..666902582 100644 --- a/test/src/unit-convenience.cpp +++ b/test/src/unit-convenience.cpp @@ -37,6 +37,84 @@ using nlohmann::json; namespace { +struct alt_string_iter +{ + alt_string_iter() = default; + alt_string_iter(const char* cstr) + : impl(cstr) + {} + + void reserve(std::size_t s) + { + impl.reserve(s); + } + + template + void append(Iter first, Iter last) + { + impl.append(first, last); + } + + std::string::const_iterator begin() const + { + return impl.begin(); + } + + std::string::const_iterator end() const + { + return impl.end(); + } + + std::size_t size() const + { + return impl.size(); + } + + alt_string_iter& operator+=(const char c) + { + impl += c; + return *this; + } + + std::string impl{}; +}; + +struct alt_string_data +{ + alt_string_data() = default; + alt_string_data(const char* cstr) + : impl(cstr) + {} + + void reserve(std::size_t s) + { + impl.reserve(s); + } + + void append(const char* p, std::size_t s) + { + impl.append(p, s); + } + + const char* data() const + { + return impl.data(); + } + + std::size_t size() const + { + return impl.size(); + } + + alt_string_data& operator+=(const char c) + { + impl += c; + return *this; + } + + std::string impl{}; +}; + void check_escaped(const char* original, const char* escaped = "", bool ensure_ascii = false); void check_escaped(const char* original, const char* escaped, const bool ensure_ascii) { @@ -110,4 +188,39 @@ TEST_CASE("convenience functions") CHECK_THROWS_WITH_AS(check_escaped("\xC2"), "[json.exception.type_error.316] incomplete UTF-8 string; last byte: 0xC2", json::type_error&); } + + SECTION("string concat") + { + using nlohmann::detail::concat; + + const char* expected = "Hello, world!"; + alt_string_iter hello_iter{"Hello, "}; + alt_string_data hello_data{"Hello, "}; + std::string world = "world"; + + SECTION("std::string") + { + std::string str1 = concat(hello_iter, world, '!'); + std::string str2 = concat(hello_data, world, '!'); + std::string str3 = concat("Hello, ", world, '!'); + + CHECK(str1 == expected); + CHECK(str2 == expected); + CHECK(str3 == expected); + } + + SECTION("alt_string_iter") + { + alt_string_iter str = concat(hello_iter, world, '!'); + + CHECK(str.impl == expected); + } + + SECTION("alt_string_data") + { + alt_string_data str = concat(hello_data, world, '!'); + + CHECK(str.impl == expected); + } + } }