diff --git a/src/Utils/Common.hpp b/src/Utils/Common.hpp index d41a96c4a..71b6a9535 100644 --- a/src/Utils/Common.hpp +++ b/src/Utils/Common.hpp @@ -52,7 +52,7 @@ namespace tnt { /** * Delayer of static_assert evaluation. */ -template +template constexpr bool always_false_v = false; /** diff --git a/src/Utils/Traits.hpp b/src/Utils/Traits.hpp index 8edca4264..bfc8d0c24 100644 --- a/src/Utils/Traits.hpp +++ b/src/Utils/Traits.hpp @@ -300,25 +300,17 @@ constexpr bool has_get_by_size_v = details::has_get_by_size_h::value; * 2. Call std::get otherwise. */ template -constexpr std::enable_if_t, T&> get(U& u) +constexpr std::enable_if_t, + decltype(std::declval().template get())> get(U&& u) { - return u.template get(); -} -template -constexpr std::enable_if_t, const T&> get(const U& u) -{ - return u.template get(); + return std::forward(u).template get(); } template -constexpr std::enable_if_t, T&> get(U& u) -{ - return std::get(u); -} -template -constexpr std::enable_if_t, const T&> get(const U& u) +constexpr std::enable_if_t, + decltype(std::get(std::declval()))> get(U&& u) { - return std::get(u); + return std::get(std::forward(u)); } /** @@ -336,30 +328,16 @@ constexpr std::enable_if_t, template constexpr std::enable_if_t, - decltype(std::declval().template get())> get(U& u) -{ - return u.template get(); -} - -template -constexpr std::enable_if_t, - decltype(std::declval().template get())> get(const U& u) -{ - return u.template get(); -} - -template -constexpr std::enable_if_t && !has_get_by_size_v, - decltype(std::get(std::declval()))> get(U& u) + decltype(std::declval().template get())> get(U&& u) { - return std::get(u); + return std::forward(u).template get(); } template constexpr std::enable_if_t && !has_get_by_size_v, - decltype(std::get(std::declval()))> get(const U& u) + decltype(std::get(std::declval()))> get(U&& u) { - return std::get(u); + return std::get(std::forward(u)); } /** diff --git a/src/mpp/ClassRule.hpp b/src/mpp/ClassRule.hpp new file mode 100644 index 000000000..1f52ad382 --- /dev/null +++ b/src/mpp/ClassRule.hpp @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2023 Tarantool AUTHORS: please see AUTHORS file. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the + * following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#pragma once + +#include "../Utils/Common.hpp" + +template struct mpp_rule; +template struct mpp_enc_rule; +template struct mpp_dec_rule; + +namespace mpp { + +namespace details { +template +struct has_member_base { + static constexpr bool value = !std::is_member_pointer_v; +}; + +template +struct has_member_rule_h : std::false_type {}; +template +struct has_member_rule_h> + : has_member_base {}; + +template +struct has_member_enc_rule_h : std::false_type {}; +template +struct has_member_enc_rule_h> + : has_member_base {}; + +template +struct has_member_dec_rule_h : std::false_type {}; +template +struct has_member_dec_rule_h> + : has_member_base {}; + +template +struct has_spec_rule_h : std::false_type {}; +template +struct has_spec_rule_h::value)>> + : has_member_base::value)> {}; + +template +struct has_spec_enc_rule_h : std::false_type {}; +template +struct has_spec_enc_rule_h::value)>> + : has_member_base::value)> {}; + +template +struct has_spec_dec_rule_h : std::false_type {}; +template +struct has_spec_dec_rule_h::value)>> + : has_member_base::value)> {}; +} // namespace details + +template +constexpr bool has_enc_rule_v = + details::has_member_rule_h::value || + details::has_member_enc_rule_h::value || + details::has_spec_rule_h::value || + details::has_spec_enc_rule_h::value; + +template +constexpr bool has_dec_rule_v = + details::has_member_rule_h::value || + details::has_member_dec_rule_h::value || + details::has_spec_rule_h::value || + details::has_spec_dec_rule_h::value; + +template +constexpr auto& get_enc_rule() +{ + if constexpr (details::has_member_enc_rule_h::value) + return T::mpp_enc; + else if constexpr (details::has_member_rule_h::value) + return T::mpp; + else if constexpr (details::has_spec_enc_rule_h::value) + return mpp_enc_rule::value; + else if constexpr (details::has_spec_rule_h::value) + return mpp_rule::value; + else + static_assert(tnt::always_false_v, + "Failed to find a rule for given type"); +} + +template +constexpr auto& get_dec_rule() +{ + if constexpr (details::has_member_dec_rule_h::value) + return T::mpp_dec; + else if constexpr (details::has_member_rule_h::value) + return T::mpp; + else if constexpr (details::has_spec_dec_rule_h::value) + return mpp_dec_rule::value; + else if constexpr (details::has_spec_rule_h::value) + return mpp_rule::value; + else + static_assert(tnt::always_false_v, + "Failed to find a rule for given type"); +} + +} // namespace mpp diff --git a/src/mpp/Dec.hpp b/src/mpp/Dec.hpp index 0c6dea434..19d17038c 100644 --- a/src/mpp/Dec.hpp +++ b/src/mpp/Dec.hpp @@ -35,6 +35,7 @@ #include #include +#include "ClassRule.hpp" #include "Constants.hpp" #include "Rules.hpp" #include "Spec.hpp" @@ -77,7 +78,8 @@ template constexpr auto detectFamily() { using U = unwrap_t; - static_assert(!std::is_const_v, "Can't decode to constant type"); + static_assert(!std::is_const_v || tnt::is_tuplish_v, + "Can't decode to constant type"); if constexpr (is_wrapped_family_v) { return family_sequence{}; } else if constexpr (std::is_same_v) { @@ -201,88 +203,140 @@ using path_push_t = STATIC_SIZE, STATIC_POS)>; -template -struct ResolverBase { - explicit constexpr ResolverBase(nullptr_t) {}; -}; +template +struct Resolver { + template + static constexpr size_t ITEM = tnt::iseq::template get(); + static constexpr size_t P0 = ITEM<0>; + static constexpr size_t PI = ITEM; + static constexpr enum path_item_type TYPE = path_item_type(PI); + static constexpr size_t POS = path_item_static_pos(PI); + static constexpr size_t SIZE = path_item_static_size(PI); + static constexpr size_t USR_ARG_COUNT = path_item_static_size(P0); + static_assert(path_item_type(P0) == PIT_STATIC_L0); + static_assert((is_path_item_static(PI) && POS < SIZE) || + (!is_path_item_static(PI) && POS + SIZE == 0)); + + template + static constexpr size_t dyn_arg_pos(tnt::iseq) + { + constexpr size_t X = + ((is_path_item_dynamic(ITEM) ? 1 : 0) + ... + 0); + return USR_ARG_COUNT + X; + } -template <> -struct ResolverBase { - uint64_t pos_size; - explicit constexpr ResolverBase(uint64_t ps) : pos_size(ps) {}; -}; + static constexpr size_t dyn_arg_pos() + { + return dyn_arg_pos(tnt::make_iseq{}); + } -template -struct Resolver : ResolverBase { - using Base = ResolverBase; - template - explicit constexpr Resolver(T t) : Base(t) { - if constexpr (is_path_item_dyn_size_pos(P)) - static_assert(std::is_same_v); - else - static_assert(std::is_same_v); + template + static constexpr size_t find_obj_index() + { + using E = decltype(extract(std::declval()...)); + using R = unwrap_t; + if constexpr (has_dec_rule_v) { + return I; + } else if constexpr (std::is_member_pointer_v || + tnt::is_tuplish_v) { + using PrevResolver = Resolver; + return PrevResolver::template find_obj_index(); + } else { + static_assert(tnt::always_false_v); + } + } + + template + static constexpr auto&& prev(T... t) + { + return unwrap(Resolver::get(t...)); + } + + template + static constexpr auto&& extract(T... t) + { + if constexpr (TYPE == PIT_STATIC_L0) { + return std::get(std::tie(t...)).get(); + } else if constexpr (is_path_item_static(PI)) { + return tnt::get(prev(t...)); + } else if constexpr (TYPE == PIT_DYN_POS) { + constexpr size_t ARG_POS = dyn_arg_pos(); + uint64_t arg = std::get(std::tie(t...)); + return std::data(prev(t...))[arg >> 32]; + } else if constexpr (TYPE == PIT_DYN_BACK) { + return prev(t...).back(); + } else if constexpr (TYPE == PIT_DYN_ADD) { + return prev(t...); + } else if constexpr (TYPE == PIT_DYN_KEY) { + return prev(t...); + } else { + static_assert(tnt::always_false_v); + } } - template - friend constexpr auto&& operator>>(W&& w, [[maybe_unused]] Resolver

r) + template + static constexpr auto&& unrule(T... t) { - auto&& t = unwrap(std::forward(w)); - using T = decltype(t); - constexpr enum path_item_type TYPE = path_item_type(P); - constexpr size_t POS = path_item_static_pos(P); - constexpr size_t SIZE = path_item_static_size(P); - static_assert((is_path_item_static(P) && POS < SIZE) || - (!is_path_item_static(P) && POS + SIZE == 0)); - if constexpr (TYPE == PIT_STATIC_L0) - return tnt::get(std::forward(t)).get(); - else if constexpr (is_path_item_static(P)) - return tnt::get(std::forward(t)); - else if constexpr (TYPE == PIT_DYN_POS) - return std::data(t)[r.pos_size >> 32]; - else if constexpr (TYPE == PIT_DYN_BACK) - return t.back(); - else if constexpr (TYPE == PIT_DYN_ADD) - return t; - else if constexpr (TYPE == PIT_DYN_KEY) - return t; + using E = decltype(extract(std::declval()...)); + using R = unwrap_t; + if constexpr (has_dec_rule_v) + return get_dec_rule(); else - static_assert(tnt::always_false_v); + return extract(t...); } -}; -template -constexpr auto dyn_arg(tnt::iseq p, tnt::iseq, T&&... t) -{ - constexpr size_t USR_ARG_COUNT = path_item_static_size(p.first()); - constexpr size_t DYN_ARG_COUNT = ((is_path_item_dynamic(P) ? 1 : 0) + ...); - constexpr size_t X = (((is_path_item_dynamic(P) && (J < I)) ? 1 : 0) + ...); - static_assert(USR_ARG_COUNT + DYN_ARG_COUNT == sizeof...(t)); - static_assert(X <= DYN_ARG_COUNT); - if constexpr (is_path_item_dyn_size_pos(Q)) - return std::get(std::forward_as_tuple(t...)); - else - return nullptr; -} + template + static constexpr auto&& self_unwrap(T&& t) + { + using R = unwrap_t; + if constexpr (has_dec_rule_v) { + using RULE = unwrap_t())>; + if constexpr (std::is_member_pointer_v) { + return self_unwrap(unwrap(std::forward(t)).* + unwrap(get_dec_rule())); + } else { + return std::forward(t); + } + } else { + return std::forward(t); + } + } -template -constexpr auto&& path_resolve(tnt::iseq p, tnt::iseq s, T&&... t) -{ - return (std::tuple(std::forward(t)...) >> - ... >> Resolver

{dyn_arg(p, s, std::forward(t)...)}); -} + template + static constexpr auto&& get(T... t) + { + using U = decltype(unrule(std::declval()...)); + using R = unwrap_t; + if constexpr (std::is_member_pointer_v) { + constexpr size_t OBJ_I = find_obj_index(); + using Res = Resolver; + return self_unwrap(unwrap(Res::extract(t...)).* + unwrap(unrule(t...))); + } else { + return unrule(t...); + } + } + + static constexpr size_t expected_arg_count() + { + return dyn_arg_pos() + (is_path_item_dynamic(ITEM) ? 1 : 0); + } +}; template -constexpr auto&& path_resolve(tnt::iseq path, T&&... t) +constexpr auto&& path_resolve(tnt::iseq, T... t) { - return path_resolve(path, tnt::make_iseq{}, - std::forward(t)...); + using Res = Resolver; + static_assert(Res::expected_arg_count() == sizeof...(T)); + return Res::get(t...); } -template -constexpr auto&& path_resolve_arg(tnt::iseq path, tnt::iseq, T&&... t) +template +constexpr auto&& path_resolve_parent(tnt::iseq, T... t) { - return path_resolve(path, tnt::make_iseq{}, - tnt::get(std::forward_as_tuple(t...))...); + using Res = Resolver; + static_assert(Res::expected_arg_count() == sizeof...(T) - 1); + return Res::get(t...); } constexpr size_t SIMPLEX_SUBRULE = 16; @@ -691,8 +745,7 @@ bool decode_next(BUF& buf, T... t) if constexpr (LAST_TYPE == PIT_DYN_POS || LAST_TYPE == PIT_DYN_ADD || LAST_TYPE == PIT_DYN_BACK) { - auto is = tnt::make_iseq{}; - auto&& parent = path_resolve_arg(POP_PATH{}, is, t...); + auto&& parent = path_resolve_parent(POP_PATH{}, t...); using par_t = std::remove_reference_t; auto& arg = std::get(std::tie(t...)); [[maybe_unused]] size_t pos = arg >> 32; diff --git a/src/mpp/Enc.hpp b/src/mpp/Enc.hpp index ebcad6ada..894377f33 100644 --- a/src/mpp/Enc.hpp +++ b/src/mpp/Enc.hpp @@ -37,6 +37,7 @@ #include #include "BSwap.hpp" +#include "ClassRule.hpp" #include "Constants.hpp" #include "Rules.hpp" #include "Spec.hpp" @@ -506,6 +507,30 @@ encode(CONT &cont, tnt::CStr prefix, tnt::iseq) return true; } +template +auto subst_tuple(const S& s, const T& t, tnt::iseq); + +template +auto subst(const S& s, [[maybe_unused]] const T& t) +{ + if constexpr (tnt::is_pairish_v) { + return std::make_pair(subst(s.first, t), subst(s.second, t)); + } else if constexpr (tnt::is_tuplish_v) { + return subst_tuple(s, t, tnt::make_iseq>{}); + } else { + if constexpr (std::is_member_pointer_v) + return t.*s; + else + return s; + } +} + +template +auto subst_tuple(const S& s, const T& t, tnt::iseq) +{ + return std::make_tuple(subst(tnt::get(s), t)...); +} + template bool encode(CONT &cont, tnt::CStr prefix, @@ -516,6 +541,9 @@ encode(CONT &cont, tnt::CStr prefix, using U = std::remove_cv_t>; if constexpr(std::is_same_v) { return encode(cont, prefix, ais, more...); + } if constexpr(mpp::has_enc_rule_v) { + const auto& rule = mpp::get_enc_rule(); + return encode(cont, prefix, ais, subst(rule, t), more...); } else if constexpr(is_wrapped_raw_v) { if constexpr(std::is_base_of_v) { using V = typename U::type; @@ -557,10 +585,18 @@ encode(CONT &cont, tnt::CStr prefix, const V& v = u.v; if constexpr(tnt::is_tuplish_v) { tnt::iseq<> is; + constexpr size_t N = sizeof...(I); + [[maybe_unused]] auto reorder = + std::tie(tnt::get(v).first..., + tnt::get(v).second...); + [[maybe_unused]] auto reindex = + [](size_t i, size_t n) { + return i / 2 + n * (i % 2); + }; return encode(cont, prefix, is, - tnt::get(v).first..., - tnt::get(v).second..., - more...); + tnt::get(reorder)..., + tnt::get(reorder)..., + more...); } else if constexpr(tnt::is_const_iterable_v) { auto itr = std::begin(v); auto e = std::end(v); diff --git a/test/CommonUnitTest.cpp b/test/CommonUnitTest.cpp index 23760a2c2..3993904af 100644 --- a/test/CommonUnitTest.cpp +++ b/test/CommonUnitTest.cpp @@ -94,5 +94,7 @@ test_tuple_utils() int main() { static_assert(tnt::always_false_v == false); + static_assert(tnt::always_false_v == false); + static_assert(tnt::always_false_v<> == false); test_tuple_utils(); } diff --git a/test/EncDecTest.cpp b/test/EncDecTest.cpp index e34fa20cb..e0e23472b 100644 --- a/test/EncDecTest.cpp +++ b/test/EncDecTest.cpp @@ -425,8 +425,8 @@ test_basic() std::forward_as_tuple(10, true, 11, "val", 12, std::make_tuple(1, 2, 3)))); // String keys. - mpp::encode(buf, mpp::as_map(std::make_tuple("key1", "val1", - "key2", "val2"))); + mpp::encode(buf, std::make_tuple(std::make_pair("key1", "val1"), + std::make_pair("key2", "val2"))); // Mixed keys. mpp::encode(buf, mpp::as_map(std::make_tuple(1, "val1", "key2", "val2"))); @@ -1011,10 +1011,220 @@ test_basic() } } +struct JustClass { + int mpp; + using mpp_dec = int; +}; + +struct WithMppMemberRule { + int i; + static constexpr auto mpp = &WithMppMemberRule::i; +}; + +struct WithEncMemberRule { + int j; + static constexpr auto mpp_enc = std::make_tuple(&WithEncMemberRule::j); +}; + +struct WithDecMemberRule { + int k; + static constexpr auto mpp_dec = std::make_tuple(&WithDecMemberRule::k); +}; + +struct WithMppSpecRule { + int a; +}; + +struct WithEncSpecRule { + int b; +}; + +struct WithDecSpecRule { + int c; +}; + +template <> +struct mpp_rule { + static constexpr auto value = &WithMppSpecRule::a; +}; + +template <> +struct mpp_enc_rule { + static constexpr auto value = std::make_tuple(&WithEncSpecRule::b); +}; + +template <> +struct mpp_dec_rule { + static constexpr auto value = std::make_tuple(&WithDecSpecRule::c); +}; + +void test_class_rules() +{ + TEST_INIT(0); + static_assert(!mpp::has_enc_rule_v); + static_assert(!mpp::has_dec_rule_v); + + static_assert(mpp::has_enc_rule_v); + static_assert(mpp::has_dec_rule_v); + static_assert(mpp::has_enc_rule_v); + static_assert(!mpp::has_dec_rule_v); + static_assert(!mpp::has_enc_rule_v); + static_assert(mpp::has_dec_rule_v); + + static_assert(mpp::has_enc_rule_v); + static_assert(mpp::has_dec_rule_v); + static_assert(mpp::has_enc_rule_v); + static_assert(!mpp::has_dec_rule_v); + static_assert(!mpp::has_enc_rule_v); + static_assert(mpp::has_dec_rule_v); + + fail_unless(&mpp::get_enc_rule() == + &WithMppMemberRule::mpp); + fail_unless(&mpp::get_dec_rule() == + &WithMppMemberRule::mpp); + fail_unless(&mpp::get_enc_rule() == + &WithEncMemberRule::mpp_enc); + fail_unless(&mpp::get_dec_rule() == + &WithDecMemberRule::mpp_dec); + + fail_unless(&mpp::get_enc_rule() == + &mpp_rule::value); + fail_unless(&mpp::get_dec_rule() == + &mpp_rule::value); + fail_unless(&mpp::get_enc_rule() == + &mpp_enc_rule::value); + fail_unless(&mpp::get_dec_rule() == + &mpp_dec_rule::value); +} + +struct IntegerWrapper { + int i = 0; + void gen() + { + i = 42; + } + + bool operator==(const IntegerWrapper& that) const + { + return i == that.i; + } + + static constexpr auto mpp = &IntegerWrapper::i; +}; + +struct Triplet { + int a = 0; + int b = 0; + int c = 0; + + void gen(size_t i) + { + a = 3 * i + 1; + b = 3 * i + 100500; + c = 3 * i + 3; + } + + bool operator==(const Triplet& that) const + { + return std::tie(a, b, c) == std::tie(that.a, that.b, that.c); + } +}; + +template <> +struct mpp_rule { + static constexpr auto value = std::make_tuple(&Triplet::a, + &Triplet::b, + &Triplet::c); +}; + +struct Error { + int code = 0; + std::string descr; + + void gen(size_t i) + { + code = i + 1; + descr = std::to_string(code); + } + + bool operator==(const Error& that) const + { + return std::tie(code, descr) == std::tie(that.code, that.descr); + } + + static constexpr auto mpp = std::make_tuple( + std::make_pair(0, &Error::code), + std::make_pair(1, &Error::descr)); +}; + +struct Body { + std::string str; + IntegerWrapper num; + std::vector triplets; + std::vector errors; + + void gen() + { + str = "gen"; + num.gen(); + triplets.resize(2); + for (size_t i = 0; i < triplets.size(); i++) + triplets[i].gen(i); + errors.resize(3); + for (size_t i = 0; i < errors.size(); i++) + errors[i].gen(i); + } + + bool operator==(const Body& that) const + { + return std::tie(str, num, triplets, errors) == + std::tie(that.str, that.num, that.triplets, that.errors); + } + + static constexpr auto mpp = std::make_tuple( + std::make_pair(0, &Body::str), + std::make_pair(1, &Body::num), + std::make_pair(2, &Body::triplets), + std::make_pair(3, &Body::errors)); +}; + +void +test_object_codec() +{ + TEST_INIT(0); + + using Buf_t = tnt::Buffer<16 * 1024>; + Buf_t buf; + + Body wr, rd; + wr.gen(); + + mpp::encode(buf, wr); + + for (auto itr = buf.begin(); itr != buf.end(); ++itr) { + char c = itr.get(); + uint8_t u = c; + const char *h = "0123456789ABCDEF"; + if (c >= 'a' && c <= 'z') + std::cout << c; + else + std::cout << h[u / 16] << h[u % 16]; + } + std::cout << std::endl; + + auto itr = buf.begin(); + mpp::decode(itr, rd); + + fail_unless(rd == wr); + fail_unless(itr == buf.end()); +} + int main() { test_under_ints(); test_bswap(); test_type_visual(); test_basic(); + test_class_rules(); + test_object_codec(); } diff --git a/test/TraitsUnitTest.cpp b/test/TraitsUnitTest.cpp index 0c7c494e4..7a39e87ab 100644 --- a/test/TraitsUnitTest.cpp +++ b/test/TraitsUnitTest.cpp @@ -386,7 +386,7 @@ struct tuple_element { using type = std::tuple_element_t(tc) == 5); tnt::get(tc) = 3.5f; fail_unless(tnt::get(tc) == 3.5f); + + using X = std::tuple; + using CX = const X; + X x; + CX cx; + static_assert(std::is_same_v(x))>); + static_assert(std::is_same_v(cx))>); + static_assert(std::is_same_v(X{}))>); + static_assert(std::is_same_v(CX{}))>); + static_assert(std::is_same_v(x))>); + static_assert(std::is_same_v(cx))>); + static_assert(std::is_same_v(X{}))>); + static_assert(std::is_same_v(CX{}))>); } struct CustomPair { @@ -1034,7 +1047,7 @@ int main() test_integer_traits(); test_c_traits(); test_integral_constant_traits(); - test_unversal_access(); + test_universal_access(); test_tuple_pair_traits(); test_variant_traits(); test_optional_traits();