From 16913517b78ad1615357d8dff11b255d3bb52441 Mon Sep 17 00:00:00 2001 From: "M. Bahoosh" <12122474+the-moisrex@users.noreply.github.com> Date: Sun, 15 Sep 2024 10:49:14 -1000 Subject: [PATCH] Making tag_invoke a feeder instead of a producer --- .../simdjson/generic/ondemand/amalgamated.h | 4 +- .../simdjson/generic/ondemand/deserialize.h | 25 ++++---- .../simdjson/generic/ondemand/document-inl.h | 14 ++++- include/simdjson/generic/ondemand/document.h | 59 ++++++++++++------ .../simdjson/generic/ondemand/tag_invoke.h | 61 ++++++++----------- include/simdjson/generic/ondemand/value-inl.h | 24 +++++--- include/simdjson/generic/ondemand/value.h | 53 ++++++++++------ .../ondemand/ondemand_custom_types_tests.cpp | 15 ++--- 8 files changed, 148 insertions(+), 107 deletions(-) diff --git a/include/simdjson/generic/ondemand/amalgamated.h b/include/simdjson/generic/ondemand/amalgamated.h index e1ba56496..06186c1e8 100644 --- a/include/simdjson/generic/ondemand/amalgamated.h +++ b/include/simdjson/generic/ondemand/amalgamated.h @@ -27,6 +27,7 @@ // Inline definitions #include "simdjson/generic/ondemand/array-inl.h" #include "simdjson/generic/ondemand/array_iterator-inl.h" +#include "simdjson/generic/ondemand/value-inl.h" #include "simdjson/generic/ondemand/document-inl.h" #include "simdjson/generic/ondemand/document_stream-inl.h" #include "simdjson/generic/ondemand/field-inl.h" @@ -39,7 +40,6 @@ #include "simdjson/generic/ondemand/raw_json_string-inl.h" #include "simdjson/generic/ondemand/serialization-inl.h" #include "simdjson/generic/ondemand/token_iterator-inl.h" -#include "simdjson/generic/ondemand/value-inl.h" #include "simdjson/generic/ondemand/value_iterator-inl.h" -#include "simdjson/generic/ondemand/tag_invoke.h" \ No newline at end of file +#include "simdjson/generic/ondemand/tag_invoke.h" diff --git a/include/simdjson/generic/ondemand/deserialize.h b/include/simdjson/generic/ondemand/deserialize.h index faa17ffbb..ca00bbad8 100644 --- a/include/simdjson/generic/ondemand/deserialize.h +++ b/include/simdjson/generic/ondemand/deserialize.h @@ -67,6 +67,14 @@ class document; } } // namespace SIMDJSON_IMPLEMENTATION +struct deserialize_tag; + +template +concept deserializable = tag_invocable; + +template +concept nothrow_deserializable = nothrow_tag_invocable; + /// Deserialize Tag inline constexpr struct deserialize_tag { using value_type = SIMDJSON_IMPLEMENTATION::ondemand::value; @@ -74,26 +82,21 @@ inline constexpr struct deserialize_tag { // Customization Point for value template - requires tag_invocable, value_type&> - [[nodiscard]] constexpr simdjson_result - operator()(std::type_identity, value_type &object) const - noexcept(nothrow_tag_invocable, value_type&>) { - return tag_invoke(*this, std::type_identity{}, object); + requires deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(value_type &object, T& output) const noexcept(nothrow_deserializable) { + return tag_invoke(*this, object, output); } // Customization Point for document template - requires tag_invocable, document_type&> - [[nodiscard]] constexpr simdjson_result - operator()(std::type_identity, document_type &object) const - noexcept(nothrow_tag_invocable, document_type&>) { - return tag_invoke(*this, std::type_identity{}, object); + requires deserializable + [[nodiscard]] constexpr /* error_code */ auto operator()(document_type &object, T& output) const noexcept(nothrow_deserializable) { + return tag_invoke(*this, object, output); } // default implementations can also be done here } deserialize{}; - #endif } // namespace simdjson diff --git a/include/simdjson/generic/ondemand/document-inl.h b/include/simdjson/generic/ondemand/document-inl.h index be2465362..cb70c22c4 100644 --- a/include/simdjson/generic/ondemand/document-inl.h +++ b/include/simdjson/generic/ondemand/document-inl.h @@ -13,6 +13,7 @@ #include "simdjson/generic/ondemand/object-inl.h" #include "simdjson/generic/ondemand/raw_json_string.h" #include "simdjson/generic/ondemand/value.h" +#include "simdjson/generic/ondemand/value-inl.h" #include "simdjson/generic/ondemand/value_iterator-inl.h" #include "simdjson/generic/ondemand/deserialize.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE @@ -168,9 +169,16 @@ template<> simdjson_inline simdjson_result document::get() & noexcept { template<> simdjson_inline simdjson_result document::get() & noexcept { return get_bool(); } template<> simdjson_inline simdjson_result document::get() & noexcept { return get_value(); } -template simdjson_inline error_code document::get(T &out) & noexcept { - return get().get(out); -} +template<> simdjson_inline error_code document::get(array& out) & noexcept { return get_array().get(out); } +template<> simdjson_inline error_code document::get(object& out) & noexcept { return get_object().get(out); } +template<> simdjson_inline error_code document::get(raw_json_string& out) & noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code document::get(std::string_view& out) & noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code document::get(double& out) & noexcept { return get_double().get(out); } +template<> simdjson_inline error_code document::get(uint64_t& out) & noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code document::get(int64_t& out) & noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code document::get(bool& out) & noexcept { return get_bool().get(out); } +template<> simdjson_inline error_code document::get(value& out) & noexcept { return get_value().get(out); } + #if SIMDJSON_EXCEPTIONS template diff --git a/include/simdjson/generic/ondemand/document.h b/include/simdjson/generic/ondemand/document.h index e305b00d1..70b4c36dc 100644 --- a/include/simdjson/generic/ondemand/document.h +++ b/include/simdjson/generic/ondemand/document.h @@ -5,6 +5,7 @@ #include "simdjson/generic/ondemand/base.h" #include "simdjson/generic/ondemand/json_iterator.h" #include "simdjson/generic/ondemand/deserialize.h" +#include "simdjson/generic/ondemand/value.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE @@ -180,28 +181,18 @@ public: * @returns A value of the given type, parsed from the JSON. * @returns INCORRECT_TYPE If the JSON value is not the given type. */ - template simdjson_inline simdjson_result get() & + template + simdjson_inline simdjson_result get() & #ifdef __cpp_concepts - noexcept(tag_invocable, document&> ? nothrow_tag_invocable, document&> : true) + noexcept(deserializable ? nothrow_deserializable : true) #else noexcept #endif { -#ifdef __cpp_concepts - if constexpr (tag_invocable, document&>) { - return deserialize(std::type_identity{}, *this); - } else { -#endif // __cpp_concepts - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); -#ifdef __cpp_concepts - } -#endif + static_assert(std::is_default_constructible::value, "Cannot initialize the specified type."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } /** * @overload template simdjson_result get() & noexcept @@ -214,10 +205,13 @@ public: * is provided. */ template + simdjson_inline simdjson_result get() && #ifdef __cpp_concepts - requires (!tag_invocable, document&>) + noexcept(deserializable ? nothrow_deserializable : true) +#else + noexcept #endif - simdjson_inline simdjson_result get() && noexcept { + { static_assert(!std::is_same::value && !std::is_same::value, "You should never hold either an ondemand::array or ondemand::object without a corresponding ondemand::document being alive; that would be Undefined Behaviour."); return static_cast(*this).get(); } @@ -233,7 +227,32 @@ public: * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) & noexcept; + template + simdjson_inline error_code get(T &out) & +#ifdef __cpp_concepts + noexcept(deserializable ? nothrow_deserializable : true) +#else + noexcept +#endif + { +#ifdef __cpp_concepts + if constexpr (deserializable) { + return deserialize(*this, out); + } else { +#endif // __cpp_concepts + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#ifdef __cpp_concepts + } +#endif + } /** @overload template error_code get(T &out) & noexcept */ template simdjson_inline error_code get(T &out) && noexcept; diff --git a/include/simdjson/generic/ondemand/tag_invoke.h b/include/simdjson/generic/ondemand/tag_invoke.h index 0608d3866..3b140a6bc 100644 --- a/include/simdjson/generic/ondemand/tag_invoke.h +++ b/include/simdjson/generic/ondemand/tag_invoke.h @@ -3,6 +3,7 @@ #ifndef SIMDJSON_CONDITIONAL_INCLUDE #define SIMDJSON_TAG_INVOKE_H #include "simdjson/generic/ondemand/base.h" +#include "simdjson/generic/ondemand/deserialize.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE #ifdef __has_include #if __has_include() @@ -22,49 +23,39 @@ namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { namespace ondemand { -template - requires std::unsigned_integral -simdjson_result tag_invoke(deserialize_tag, std::type_identity, auto &val) noexcept { +template +error_code tag_invoke(deserialize_tag, auto &val, T& out) noexcept { uint64_t x; SIMDJSON_TRY(val.get_uint64().get(x)); if(x > (std::numeric_limits::max)() || x < (std::numeric_limits::min)()) { return NUMBER_OUT_OF_RANGE; } - return static_cast(x); + out = static_cast(x); + return SUCCESS; } -template - requires std::floating_point -simdjson_result tag_invoke(deserialize_tag, std::type_identity, auto &val) noexcept { +template +error_code tag_invoke(deserialize_tag, auto &val, T& out) noexcept { double x; SIMDJSON_TRY(val.get_double().get(x)); - return static_cast(x); + out = static_cast(x); + return SUCCESS; } -template - requires std::signed_integral -simdjson_result tag_invoke(deserialize_tag, std::type_identity, auto &val) noexcept { +template +error_code tag_invoke(deserialize_tag, auto &val, T& out) noexcept { int64_t x; SIMDJSON_TRY(val.get_int64().get(x)); if(x > (std::numeric_limits::max)() || x < (std::numeric_limits::min)()) { return NUMBER_OUT_OF_RANGE; } - return static_cast(x); + out = static_cast(x); + return SUCCESS; } -template - requires std::same_as -simdjson_result tag_invoke(deserialize_tag, std::type_identity, auto &val) noexcept { - T s; - SIMDJSON_TRY(val.get_string(s)); - return s; -} - -template -simdjson_result tag_invoke(deserialize_tag, std::type_identity, jsonval &val) noexcept { - std::string s; - SIMDJSON_TRY(val.get_string(s)); - return s; +error_code tag_invoke(deserialize_tag, auto &val, std::string& out) noexcept { + SIMDJSON_TRY(val.get_string(out)); + return SUCCESS; } /** @@ -75,33 +66,31 @@ simdjson_result tag_invoke(deserialize_tag, std::type_identity>(). */ -template -simdjson_result> tag_invoke(deserialize_tag, std::type_identity>, jsonval &val) noexcept { - std::vector vec; +template > +error_code tag_invoke(deserialize_tag, auto &val, std::vector& out) noexcept { array array; SIMDJSON_TRY(val.get_array().get(array)); for (auto v : array) { T value; SIMDJSON_TRY(v.get().get(value)); - vec.push_back(value); + out.push_back(value); } - return vec; + return SUCCESS; } -template -simdjson_result> tag_invoke(deserialize_tag, std::type_identity>, jsonval &val) noexcept { - std::list vec; +template > +error_code tag_invoke(deserialize_tag, auto &val, std::list& out) noexcept { array array; SIMDJSON_TRY(val.get_array().get(array)); for (auto v : array) { T value; SIMDJSON_TRY(v.get().get(value)); - vec.push_back(value); + out.push_back(value); } - return vec; + return SUCCESS; } } } } #endif // __cpp_concepts -#endif // SIMDJSON_TAG_INVOKE_H \ No newline at end of file +#endif // SIMDJSON_TAG_INVOKE_H diff --git a/include/simdjson/generic/ondemand/value-inl.h b/include/simdjson/generic/ondemand/value-inl.h index 8fbb4f8e3..2b84810cd 100644 --- a/include/simdjson/generic/ondemand/value-inl.h +++ b/include/simdjson/generic/ondemand/value-inl.h @@ -91,9 +91,16 @@ template<> simdjson_inline simdjson_result value::get() noexcept { ret template<> simdjson_inline simdjson_result value::get() noexcept { return get_int64(); } template<> simdjson_inline simdjson_result value::get() noexcept { return get_bool(); } -template simdjson_inline error_code value::get(T &out) noexcept { - return get().get(out); -} + +template<> simdjson_inline error_code value::get(array& out) noexcept { return get_array().get(out); } +template<> simdjson_inline error_code value::get(object& out) noexcept { return get_object().get(out); } +template<> simdjson_inline error_code value::get(raw_json_string& out) noexcept { return get_raw_json_string().get(out); } +template<> simdjson_inline error_code value::get(std::string_view& out) noexcept { return get_string(false).get(out); } +template<> simdjson_inline error_code value::get(number& out) noexcept { return get_number().get(out); } +template<> simdjson_inline error_code value::get(double& out) noexcept { return get_double().get(out); } +template<> simdjson_inline error_code value::get(uint64_t& out) noexcept { return get_uint64().get(out); } +template<> simdjson_inline error_code value::get(int64_t& out) noexcept { return get_int64().get(out); } +template<> simdjson_inline error_code value::get(bool& out) noexcept { return get_bool().get(out); } #if SIMDJSON_EXCEPTIONS template @@ -417,6 +424,12 @@ simdjson_inline simdjson_result simdjson_result simdjson_inline error_code simdjson_result::get(SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept { + if (error()) { return error(); } + out = first; + return SUCCESS; +} + template simdjson_inline simdjson_result simdjson_result::get() noexcept { if (error()) { return error(); } return first.get(); @@ -430,11 +443,6 @@ template<> simdjson_inline simdjson_result simdjson_inline error_code simdjson_result::get(SIMDJSON_IMPLEMENTATION::ondemand::value &out) noexcept { - if (error()) { return error(); } - out = first; - return SUCCESS; -} simdjson_inline simdjson_result simdjson_result::type() noexcept { if (error()) { return error(); } diff --git a/include/simdjson/generic/ondemand/value.h b/include/simdjson/generic/ondemand/value.h index 40f65f435..6e95aa8c0 100644 --- a/include/simdjson/generic/ondemand/value.h +++ b/include/simdjson/generic/ondemand/value.h @@ -8,6 +8,8 @@ #include "simdjson/generic/ondemand/deserialize.h" #endif // SIMDJSON_CONDITIONAL_INCLUDE +#include + namespace simdjson { namespace SIMDJSON_IMPLEMENTATION { @@ -39,28 +41,18 @@ public: template simdjson_inline simdjson_result get() #ifdef __cpp_concepts - noexcept(tag_invocable, value&> ? nothrow_tag_invocable, value&> : true) + noexcept(deserializable ? nothrow_deserializable : true) #else noexcept #endif - { -#ifdef __cpp_concepts - if constexpr (tag_invocable, value&>) { - return deserialize(std::type_identity{}, *this); - } else { -#endif // __cpp_concepts - // Unless the simdjson library or the user provides an inline implementation, calling this method should - // immediately fail. - static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " - "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " - "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " - " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." - " You may also add support for custom types, see our documentation."); -#ifdef __cpp_concepts - } -#endif + { + static_assert(std::is_default_constructible::value, "The specified type is not default constructible."); + T out{}; + SIMDJSON_TRY(get(out)); + return out; } + /** * Get this value as the given type. * @@ -70,7 +62,32 @@ public: * @returns INCORRECT_TYPE If the JSON value is not an object. * @returns SUCCESS If the parse succeeded and the out parameter was set to the value. */ - template simdjson_inline error_code get(T &out) noexcept; + template + simdjson_inline error_code get(T &out) +#ifdef __cpp_concepts + noexcept(deserializable ? nothrow_deserializable : true) +#else + noexcept +#endif + { +#ifdef __cpp_concepts + if constexpr (deserializable) { + return deserialize(*this, out); + } else { +#endif // __cpp_concepts + // Unless the simdjson library or the user provides an inline implementation, calling this method should + // immediately fail. + static_assert(!sizeof(T), "The get method with given type is not implemented by the simdjson library. " + "The supported types are ondemand::object, ondemand::array, raw_json_string, std::string_view, uint64_t, " + "int64_t, double, and bool. We recommend you use get_double(), get_bool(), get_uint64(), get_int64(), " + " get_object(), get_array(), get_raw_json_string(), or get_string() instead of the get template." + " You may also add support for custom types, see our documentation."); + static_cast(out); // to get rid of unused errors + return UNINITIALIZED; +#ifdef __cpp_concepts + } +#endif + } /** * Cast this JSON value to an array. diff --git a/tests/ondemand/ondemand_custom_types_tests.cpp b/tests/ondemand/ondemand_custom_types_tests.cpp index eff055165..d95f3f627 100644 --- a/tests/ondemand/ondemand_custom_types_tests.cpp +++ b/tests/ondemand/ondemand_custom_types_tests.cpp @@ -27,9 +27,10 @@ namespace simdjson { // This tag_invoke MUST be inside simdjson namespace template requires is_unique_ptr_v -auto tag_invoke(deserialize_tag, std::type_identity, ondemand::value &val) { +auto tag_invoke(deserialize_tag, ondemand::value &val, T& out) { using type = typename T::element_type; - return simdjson_result{std::make_unique(val.template get())}; + out = std::make_unique(val.template get()); + return SUCCESS; } } // namespace simdjson @@ -46,14 +47,12 @@ struct Car { int year{}; std::vector tire_pressure{}; - friend simdjson_result - tag_invoke(simdjson::deserialize_tag, std::type_identity, auto &val) { + friend simdjson::error_code tag_invoke(simdjson::deserialize_tag, auto &val, Car& car) { simdjson::ondemand::object obj; auto error = val.get_object().get(obj); if (error) { return error; } - Car car{}; // Instead of repeatedly obj["something"], we iterate through the object // which we expect to be faster. for (auto field : obj) { @@ -84,13 +83,11 @@ struct Car { } } } - return car; + return simdjson::SUCCESS; } }; -static_assert(simdjson::tag_invocable>, simdjson::ondemand::value &>, - "It should be invocable"); +static_assert(simdjson::deserializable>, "It should be deserializable"); bool custom_uniqueptr_test() { TEST_START();