From b0ade5a6fafd37234c276bfc84bf4e9b3c1189b1 Mon Sep 17 00:00:00 2001 From: MistEO Date: Thu, 25 Jan 2024 14:02:24 +0800 Subject: [PATCH] perf and fix --- include/json.hpp | 275 +++++++++++++++++++++++++++++++--------------- sample/sample.cpp | 100 +++++++++-------- 2 files changed, 241 insertions(+), 134 deletions(-) diff --git a/include/json.hpp b/include/json.hpp index 6fa706d..259370e 100644 --- a/include/json.hpp +++ b/include/json.hpp @@ -59,7 +59,85 @@ namespace utils constexpr bool is_collection = false; template constexpr bool is_collection = is_container && !is_map; -} + + template + class has_to_json_in_member + { + template + static auto test(int) -> decltype(std::declval().to_json(), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + class has_to_json_in_global + { + template + static auto test(int) -> decltype(to_json(std::declval()), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + class has_check_json_in_member + { + template + static auto test(int) -> decltype(std::declval().check_json(std::declval()), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + class has_check_json_in_global + { + template + static auto test(int) -> decltype(check_json(std::declval(), std::declval()), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + class has_from_json_in_member + { + template + static auto test(int) -> decltype(std::declval().from_json(std::declval()), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; + + template + class has_from_json_in_global + { + template + static auto test(int) -> decltype(from_json(std::declval(), std::declval()), std::true_type()); + + template + static std::false_type test(...); + + public: + static constexpr bool value = decltype(test(0))::value; + }; +} // namespace utils // ********************************* // * basic_value declare * @@ -126,6 +204,13 @@ class basic_value basic_value(map_t&& map) : basic_value(basic_object(std::forward(map))) {} + template ::value, bool> = true> + basic_value(const jsonization_t& jsonization) : basic_value(jsonization.to_json()) + {} + template ::value, bool> = true> + basic_value(const jsonization_t& jsonization) : basic_value(to_json(jsonization)) + {} + template >, bool> = true> basic_value(value_t) = delete; @@ -345,6 +430,12 @@ class basic_array basic_array(collection_t arr) : _array_data(std::make_move_iterator(arr.begin()), std::make_move_iterator(arr.end())) {} + template ::value, bool> = true> + basic_array(const jsonization_t& jsonization) : basic_array(jsonization.to_json()) + {} + template ::value, bool> = true> + basic_array(const jsonization_t& jsonization) : basic_array(to_json(jsonization)) + {} ~basic_array() noexcept = default; @@ -461,6 +552,12 @@ class basic_object typename = std::enable_if_t>>> basic_object(map_t map) : _object_data(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end())) {} + template ::value, bool> = true> + basic_object(const jsonization_t& jsonization) : basic_object(jsonization.to_json()) + {} + template ::value, bool> = true> + basic_object(const jsonization_t& jsonization) : basic_object(to_json(jsonization)) + {} ~basic_object() = default; @@ -803,6 +900,12 @@ inline bool basic_value::is() const noexcept else if constexpr (std::is_constructible_v) { return is_string(); } + else if constexpr (utils::has_check_json_in_member::value) { + return value_t().check_json(*this); + } + else if constexpr (utils::has_check_json_in_global::value) { + return check_json(*this, value_t()); + } else { static_assert(!sizeof(value_t), "Unsupported type"); } @@ -1127,6 +1230,20 @@ inline value_t basic_value::as() const if constexpr (std::is_same_v, value_t>) { return *this; } + else if constexpr (utils::has_from_json_in_member::value) { + value_t val; + if (!val.from_json(*this)) { + throw exception("Wrong JSON"); + } + return val; + } + else if constexpr (utils::has_from_json_in_global::value) { + value_t val; + if (!from_json(*this, val)) { + throw exception("Wrong JSON"); + } + return val; + } else { return static_cast(*this); } @@ -2788,106 +2905,68 @@ struct next_is_optional_t struct va_arg_end {}; -template -class has_jsonization_in_member -{ - template - static auto test(int) -> decltype(std::declval().to_json(), - std::declval().from_json(std::declval()), std::true_type()); - - template - static std::false_type test(...); - -public: - static constexpr bool value = decltype(test(0))::value; -}; - -template -class has_jsonization_in_global +class dumper { - template - static auto test(int) -> decltype(to_json(std::declval()), - from_json(std::declval(), std::declval()), std::true_type()); - - template - static std::false_type test(...); - public: - static constexpr bool value = decltype(test(0))::value; -}; - -struct string_converter -{ - template - static constexpr bool is_convertible = - has_jsonization_in_member::value || has_jsonization_in_global::value; - - template - auto operator()(input_t&& arg) const + template + json::value _to_json(const char* key, const var_t& var, rest_t&&... rest) const { - if constexpr (has_jsonization_in_member::value) { - return std::forward(arg).to_json(); - } - else if constexpr (has_jsonization_in_global::value) { - return to_json(std::forward(arg)); - } - else { - static_assert(!sizeof(input_t), "Unable to convert"); - } + json::value result = _to_json(std::forward(rest)...); + result.emplace(key, var); + return result; + } + template + json::value _to_json(const char*, next_is_optional_t, rest_t&&... rest) const + { + return _to_json(std::forward(rest)...); } + json::value _to_json(va_arg_end) const { return {}; } }; -class dumper +class checker { public: template - json::object _to_json(const char* key, const var_t& var, rest_t&&... rest) const + bool _check_json(const json::value& in, std::string& error_key, const char* key, var_t&, rest_t&&... rest) { - json::object result = _to_json(key, std::forward(rest)...); - - if constexpr (has_jsonization_in_member::value) { - result.emplace(key, var.to_json()); - } - else if constexpr (has_jsonization_in_global::value) { - result.emplace(key, to_json(var)); + if (auto opt = in.find(key)) { + if (!opt->is()) { + error_key = key; + return false; + } + _next_is_optional = false; } - else if constexpr (!std::is_same_v) { - result.emplace(key, json::serialize(var, _string_converter)); + else if (!_next_is_optional) { + error_key = key; + return false; } - return result; + return _check_json(in, error_key, std::forward(rest)...); + } + template + bool _check_json(const json::value& in, std::string& error_key, const char*, next_is_optional_t, rest_t&&... rest) + { + _next_is_optional = true; + return _check_json(in, error_key, std::forward(rest)...); } - json::object _to_json(const char*, va_arg_end) const { return {}; } + bool _check_json(const json::value&, std::string&, va_arg_end) { return true; } private: - string_converter _string_converter {}; + bool _next_is_optional = false; }; class loader { public: template - bool _from_json(const json::object& in, std::string& error_key, const char* key, var_t& var, rest_t&&... rest) + bool _from_json(const json::value& in, std::string& error_key, const char* key, var_t& var, rest_t&&... rest) { if (auto opt = in.find(key)) { - if constexpr (has_jsonization_in_member::value) { - if (!opt->is_object() || !var.from_json(opt->as_object())) { - error_key = key; - return false; - } - } - else if constexpr (has_jsonization_in_global::value) { - if (!opt->is_object() || !from_json(opt->as_object(), var)) { - error_key = key; - return false; - } - } - else { - if (!opt->is()) { - error_key = key; - return false; - } - var = std::move(opt)->as(); + if (!opt->is()) { + auto test = opt->dumps(); + error_key = key; + return false; } + var = std::move(opt)->as(); _next_is_optional = false; } else if (!_next_is_optional) { @@ -2897,12 +2976,12 @@ class loader return _from_json(in, error_key, std::forward(rest)...); } template - bool _from_json(const json::object& in, std::string& error_key, const char*, next_is_optional_t, rest_t&&... rest) + bool _from_json(const json::value& in, std::string& error_key, const char*, next_is_optional_t, rest_t&&... rest) { _next_is_optional = true; return _from_json(in, error_key, std::forward(rest)...); } - bool _from_json(const json::object&, std::string&, va_arg_end) { return true; } + bool _from_json(const json::value&, std::string&, va_arg_end) { return true; } private: bool _next_is_optional = false; @@ -3003,22 +3082,42 @@ namespace json::_private_macro } // namespace json::_private_macro // if you are using MSVC, please add "/Zc:preprocessor" to your project -#define MEO_JSONIZATION(...) \ - json::object to_json() const \ - { \ - return json::_jsonization_helper::dumper()._to_json(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \ - json::_jsonization_helper::va_arg_end()); \ - } \ - bool from_json(const json::object& _MEOJSON_VARNAME(in)) \ +#define MEO_TOJSON(...) \ + json::value to_json() const \ + { \ + return json::_jsonization_helper::dumper()._to_json(_MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \ + json::_jsonization_helper::va_arg_end {}); \ + } + +// if you are using MSVC, please add "/Zc:preprocessor" to your project +#define MEO_CHECKJSON(...) \ + bool check_json(const json::value& _MEOJSON_VARNAME(in)) const \ + { \ + std::string _MEOJSON_VARNAME(error_key); \ + return check_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \ + } \ + bool check_json(const json::value& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) const \ + { \ + return json::_jsonization_helper::checker()._check_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \ + _MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \ + json::_jsonization_helper::va_arg_end {}); \ + } + +// if you are using MSVC, please add "/Zc:preprocessor" to your project +#define MEO_FROMJSON(...) \ + bool from_json(const json::value& _MEOJSON_VARNAME(in)) \ { \ std::string _MEOJSON_VARNAME(error_key); \ return from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key)); \ } \ - bool from_json(const json::object& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) \ + bool from_json(const json::value& _MEOJSON_VARNAME(in), std::string& _MEOJSON_VARNAME(error_key)) \ { \ return json::_jsonization_helper::loader()._from_json(_MEOJSON_VARNAME(in), _MEOJSON_VARNAME(error_key), \ _MEOJSON_FOR_EACH(_MEOJSON_KEY_VALUE, __VA_ARGS__) \ - json::_jsonization_helper::va_arg_end()); \ + json::_jsonization_helper::va_arg_end {}); \ } +// if you are using MSVC, please add "/Zc:preprocessor" to your project +#define MEO_JSONIZATION(...) MEO_TOJSON(__VA_ARGS__) MEO_CHECKJSON(__VA_ARGS__) MEO_FROMJSON(__VA_ARGS__) + #define MEO_OPT json::_jsonization_helper::next_is_optional_t {}, diff --git a/sample/sample.cpp b/sample/sample.cpp index 455759c..34245cd 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -15,17 +15,17 @@ void test_jsonization(); int main() { - std::cout << "\n****** Parsing ******\n" << std::endl; + // std::cout << "\n****** Parsing ******\n" << std::endl; - if (!parsing()) { - return -1; - } + // if (!parsing()) { + // return -1; + // } - std::cout << "\n****** Serializing ******\n" << std::endl; + // std::cout << "\n****** Serializing ******\n" << std::endl; - if (!serializing()) { - return -1; - } + // if (!serializing()) { + // return -1; + // } test_jsonization(); @@ -297,64 +297,72 @@ bool serializing() return true; } -struct StructureFromOtherLibrary // like cv::Mat +struct TypeFromOtherLibrary { int a_i = 100; }; -json::object to_json(const StructureFromOtherLibrary&) + +namespace json { - return { { "aaa", "ai" } }; +json::value to_json(const TypeFromOtherLibrary& t) +{ + return t.a_i; } -bool from_json(const json::object&, StructureFromOtherLibrary& aa) +bool check_json(const json::value& j, TypeFromOtherLibrary) { - aa.a_i = -1; - return true; + return j.is_number(); } - -struct MyCustomStructure +bool from_json(const json::value& j, TypeFromOtherLibrary& out) { - int b_i = 10; - - json::object to_json() const { return { { "bbb", "bi" } }; } - bool from_json(const json::object& in) - { - std::ignore = in; - b_i = -100; - return true; - } -}; + out.a_i = j.as_integer(); + return true; +} +} void test_jsonization() { - struct MyStruct + struct AAA { - std::vector vec; - std::map map; - int i = 0; - double d = 0; + int a_i = 100; - StructureFromOtherLibrary structureFromOtherLibrary; - MyCustomStructure myCustomStructure; + TypeFromOtherLibrary other; - MEO_JSONIZATION(vec, map, MEO_OPT i, MEO_OPT d, structureFromOtherLibrary, myCustomStructure); + MEO_JSONIZATION(a_i, other); }; - MyStruct a; + struct BBB + { + int b_i = 10; + double b_d = 0.5; + + AAA b_aaa; - a.vec = { 1, 2, 3 }; - a.map = { { "key", 5 } }; - a.i = 100; - a.d = 0.5; + MEO_JSONIZATION(b_i, MEO_OPT b_d, b_aaa); + }; - json::object j = a.to_json(); + std::vector my_vec(3); + json::value j = my_vec; std::cout << j << std::endl; - // for test MEO_OPT - j.erase("i"); + std::vector result(j); + std::cout << json::value(result) << std::endl; + + // MyStruct a; + + // a.vec = { 1, 2, 3 }; + // a.map = { { "key", 5 } }; + // a.i = 100; + // a.d = 0.5; + + // json::object j = a.to_json(); + // std::cout << j << std::endl; + + //// for test MEO_OPT + // j.erase("i"); - MyStruct b; - bool loaded = b.from_json(j); + // MyStruct b; + // bool loaded = b.from_json(j); - std::cout << "loaded: " << loaded << std::endl; - std::cout << b.myCustomStructure.b_i << std::endl; + // std::cout << "loaded: " << loaded << std::endl; + // std::cout << b.myCustomStructure.b_i << std::endl; }