diff --git a/src/mpp/Dec.hpp b/src/mpp/Dec.hpp index fd0074a96..278013b52 100644 --- a/src/mpp/Dec.hpp +++ b/src/mpp/Dec.hpp @@ -253,6 +253,24 @@ using path_push_t = STATIC_SIZE, STATIC_POS)>; +/* + * Construct a pair of two references to two given objects. + */ +template +static constexpr std::pair ref_pair(T& t, U& u) +{ + return std::pair{t, u}; +} + +/* + * Construct a pair of two references to given object and to std::ignore. + */ +template +static constexpr std::pair ref_uno(T& t) +{ + return ref_pair(t, std::ignore); +} + template struct Resolver { template @@ -270,6 +288,7 @@ struct Resolver { static_assert((requires_pos_and_size && POS < SIZE) || (!requires_pos_and_size && POS + SIZE == 0)); + /** Helper of dyn_arg_pos(). */ template static constexpr size_t dyn_arg_pos(tnt::iseq) { @@ -278,104 +297,101 @@ struct Resolver { return USR_ARG_COUNT + X; } + /** + * Find index of an argument that represents dynamic position of + * current (Ith) path element. + */ static constexpr size_t dyn_arg_pos() { return dyn_arg_pos(tnt::make_iseq{}); } + /** + * Base resolver. Simply resolve Ith element of path, which along + * with general types can return reference to an object (which is + * expected to have decoding rules) or pointer to member. + * Return a pair, with the resolved thing as the first member. The + * second member of the pair is exactly what was got from resolving + * the parent, (I-1)th element of the path (std::ignore if I == 0). + */ 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) + static constexpr auto get_raw(T... t) { if constexpr (TYPE == PIT_STATIC_L0) { - return std::get(std::tie(t...)).get(); + return ref_uno(std::get(std::tie(t...)).get()); } else if constexpr (is_path_item_static(PI)) { - return tnt::get(prev(t...)); + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); + return ref_pair(tnt::get(b), p.second); } else if constexpr (TYPE == PIT_DYN_POS) { + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); constexpr size_t ARG_POS = dyn_arg_pos(); uint64_t arg = std::get(std::tie(t...)); - return std::data(prev(t...))[arg >> 32]; + return ref_pair(std::data(b)[arg >> 32], p.second); } else if constexpr (TYPE == PIT_DYN_BACK) { - return prev(t...).back(); + auto p = Resolver::get_pair(t...); + auto& b = unwrap(p.first); + return ref_pair(b.back(), p.second); } else if constexpr (TYPE == PIT_DYN_ADD) { - return prev(t...); + return Resolver::get_pair(t...); } else if constexpr (TYPE == PIT_DYN_KEY) { - return prev(t...); + return Resolver::get_pair(t...); } else if constexpr (TYPE == PIT_OPTIONAL) { - auto &&opt = prev(t...); + auto p = Resolver::get_pair(t...); + auto& opt = unwrap(p.first); assert(opt.has_value()); - return *opt; + return ref_pair(*opt, p.second); } else if constexpr (TYPE == PIT_VARIANT) { - return tnt::get(prev(t...)); + auto p = Resolver::get_pair(t...); + auto& var = unwrap(p.first); + return ref_pair(tnt::get(var), p.second); } else { static_assert(tnt::always_false_v); } } - template - static constexpr auto&& unrule(T... t) - { - using E = decltype(extract(std::declval()...)); - using R = unwrap_t; - if constexpr (has_dec_rule_v) - return get_dec_rule(); - else - return extract(t...); - } - - template - static constexpr auto&& self_unwrap(T&& t) + /** + * Transform a pair that was got from the get_raw method above. + * The result is also a pair of references. + * 1. If the first member of pair is an object with decoding rule - + * create pair with decoding rule and the object and recall this + * function recursively. + * 2. If the first member of pair is pointer to member - apply it to + * object in the second member. Create a pair with the result as the + * first member and recall this function recursively. + * 3. Return original pair otherwise. + */ + template + static constexpr auto transform_raw(std::pair p) { 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); - } + auto q = ref_pair(get_dec_rule(), p.first); + return transform_raw(q); + } else if constexpr (std::is_member_pointer_v) { + auto q = ref_uno(unwrap(p.second).*unwrap(p.first)); + return transform_raw(q); } else { - return std::forward(t); + return p; } } + /** Get transformed pair. */ template - static constexpr auto&& get(T... t) + static constexpr auto get_pair(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...); - } + return transform_raw(get_raw(t...)); + } + + /** Resolve path to Ith element. */ + template + static constexpr auto& get(T... t) + { + return get_pair(t...).first; } + /** Get the expected number of argument in get(T...) above. */ static constexpr size_t expected_arg_count() { return dyn_arg_pos() + (is_path_item_dynamic(ITEM) ? 1 : 0); @@ -704,7 +720,7 @@ struct JumpsBuilder { } else if constexpr (path_item_type(LAST) == PIT_DYN_KEY) { using R = decltype(path_resolve(PATH{}, std::declval()...)); - using DST = std::remove_reference_t; + using DST = unwrap_t; static_assert(tnt::is_tuplish_v); constexpr size_t S = tnt::tuple_size_v; if constexpr (tnt::is_tuplish_of_pairish_v) { @@ -789,8 +805,8 @@ decode_impl(BUF& buf, T... t) static_assert(path_item_type(PATH::last()) != PIT_BAD); if constexpr (path_item_type(PATH::last()) != PIT_DYN_SKIP && path_item_type(PATH::last()) != PIT_RAW) { - auto&& wrapped_dst = path_resolve(PATH{}, t...); - using wrapped_dst_t = std::remove_reference_t; + using wrapped_dst_t = std::remove_reference_t< + decltype(path_resolve(PATH{}, t...))>; if constexpr (is_raw_decoded_v) { return decode_raw(buf, t...); } else { @@ -1131,9 +1147,9 @@ template auto&& key_path_resolve(DST&& dst) { if constexpr (PAIRS) - return tnt::get<0>(tnt::get(dst)); + return tnt::get<0>(tnt::get(unwrap(dst))); else - return tnt::get(dst); + return tnt::get(unwrap(dst)); } template ; + using dst_t = unwrap_t; static_assert(tnt::is_tuplish_v); constexpr bool PAIRS = tnt::is_tuplish_of_pairish_v; diff --git a/test/EncDecTest.cpp b/test/EncDecTest.cpp index 8c8ad2e60..ebbcbb431 100644 --- a/test/EncDecTest.cpp +++ b/test/EncDecTest.cpp @@ -769,14 +769,16 @@ struct Triplet { c = 3 * i + 3; } + auto tie() const { return std::tie(a, b, c); } + bool operator==(const Triplet& that) const { - return std::tie(a, b, c) == std::tie(that.a, that.b, that.c); + return tie() == that.tie(); } bool operator<(const Triplet& that) const { - return std::tie(a, b, c) < std::tie(that.a, that.b, that.c); + return tie() < that.tie(); } }; @@ -797,26 +799,37 @@ struct Error { descr = std::to_string(code); } - bool operator==(const Error& that) const - { - return std::tie(code, descr) == std::tie(that.code, that.descr); - } - - bool operator<(const Error& that) const - { - return std::tie(code, descr) < std::tie(that.code, that.descr); - } + auto tie() const { return std::tie(code, descr); } + bool operator==(const Error &that) const { return tie() == that.tie(); } + bool operator<(const Error &that) const { return tie() < that.tie(); } static constexpr auto mpp = std::make_tuple( std::make_pair(0, &Error::code), std::make_pair(1, &Error::descr)); }; +struct Legs { + void gen() + { + left = 31; + right = 32; + } + int left; + int right; + + auto tie() const { return std::tie(left, right); } + bool operator==(const Legs &that) const { return tie() == that.tie(); } + bool operator<(const Legs &that) const { return tie() < that.tie(); } + + static constexpr auto mpp = std::make_tuple(&Legs::left, &Legs::right); +}; + struct Body { std::string str; IntegerWrapper num; std::vector triplets; std::vector errors; + Legs legs; void gen() { @@ -828,25 +841,19 @@ struct Body { errors.resize(3); for (size_t i = 0; i < errors.size(); i++) errors[i].gen(i); + legs.gen(); } - bool operator==(const Body& that) const - { - return std::tie(str, num, triplets, errors) == - std::tie(that.str, that.num, that.triplets, that.errors); - } - - bool operator<(const Body& that) const - { - return std::tie(str, num, triplets, errors) < - std::tie(that.str, that.num, that.triplets, that.errors); - } + auto tie() const { return std::tie(str, num, triplets, errors, legs); } + bool operator==(const Body &that) const { return tie() == that.tie(); } + bool operator<(const Body &that) const { return tie() < that.tie(); } 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)); + std::make_pair(3, &Body::errors), + std::make_pair(4, &Body::legs)); }; void