diff --git a/example/dynamixel-protocol-2.0.cpp b/example/dynamixel-protocol-2.0.cpp index a011ac0..4e6dd67 100644 --- a/example/dynamixel-protocol-2.0.cpp +++ b/example/dynamixel-protocol-2.0.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -11,14 +10,57 @@ ostream &operator<<(ostream &os, byte b) { return os << static_cast(b); } } // namespace std +using crc = upd::xuint<16>; + +constexpr auto accumulate_crc(crc acc, upd::xuint<8> byte) noexcept -> crc { + constexpr auto crc_table = std::array { + 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, + 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, + 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, + 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, + 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, + 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, + 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, + 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, + 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, + 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, + 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, + 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, + 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, + 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, + 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, + 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, + 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 + }; + + auto i = ((acc >> 8) ^ byte) & 0xff; + return (acc << 8) ^ crc_table[i]; +} + constexpr auto description = [] { using namespace upd; using namespace upd::literals; using namespace upd::descriptor; - return constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) | - field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) | - field<"checksum"_h>(unsigned_int, width<16>); + return + // constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) | + // field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) | + checksum<"crc"_h>(accumulate_crc, width<16>, 0, all_fields); }(); constexpr auto answer_description = [] { @@ -29,16 +71,19 @@ constexpr auto answer_description = [] { return constant<"header"_h>(0xfdffff, width<32>) | field<"id"_h>(unsigned_int, width<8>) | field<"length"_h>(unsigned_int, width<16>) | field<"instruction"_h>(unsigned_int, width<8>) | field<"error"_h>(unsigned_int, width<8>) | field<"model_number"_h>(unsigned_int, width<16>) | - field<"firmware_version"_h>(unsigned_int, width<8>) | field<"checksum"_h>(unsigned_int, width<16>); + field<"firmware_version"_h>(unsigned_int, width<8>) | + checksum<"crc"_h>(accumulate_crc, width<16>, 0, all_fields); }(); struct serializer { + constexpr static auto bytewidth = 8; + template void serialize_unsigned(XInteger value, OutputIt output) { static_assert(upd::is_extended_integer_v, "`value` must be an instance of `extended_integer`"); static_assert(!upd::is_signed_v, "`value` must be unsigned"); - constexpr auto byte_count = value.bitsize / CHAR_BIT; + constexpr auto byte_count = value.bitsize / bytewidth; auto decomposition = value.decompose(upd::width); std::copy(decomposition.begin(), decomposition.end(), output); @@ -56,7 +101,7 @@ struct serializer { abs = ~abs + 1; } - constexpr auto byte_count = abs.bitsize / CHAR_BIT; + constexpr auto byte_count = abs.bitsize / bytewidth; auto decomposition = abs.decompose(upd::width); std::copy(decomposition.begin(), decomposition.end(), output); @@ -64,9 +109,9 @@ struct serializer { template auto deserialize_unsigned(InputIt input, upd::width_t) { - static_assert(Bitsize % CHAR_BIT == 0, "`Bitsize` must be a multiple of `CHAR_BIT`"); + static_assert(Bitsize % bytewidth == 0, "`Bitsize` must be a multiple of `bytewidth`"); - constexpr auto size = Bitsize / CHAR_BIT; + constexpr auto size = Bitsize / bytewidth; auto byteseq = std::array{}; auto last_written = std::copy_n(input, size, byteseq.begin()); @@ -78,9 +123,9 @@ struct serializer { template auto deserialize_signed(InputIt input, upd::width_t) { - static_assert((Bitsize + 1) % CHAR_BIT == 0, "`Bitsize` must be a multiple of `CHAR_BIT`"); + static_assert((Bitsize + 1) % bytewidth == 0, "`Bitsize` must be a multiple of `bytewidth`"); - constexpr auto size = (Bitsize + 1) / CHAR_BIT; + constexpr auto size = (Bitsize + 1) / bytewidth; auto byteseq = std::array{}; auto last_written = std::copy_n(input, size, byteseq.begin()); @@ -88,7 +133,7 @@ struct serializer { UPD_ASSERT(last_written == byteseq.end()); auto raw = upd::recompose_into_xuint(byteseq); - auto sign = (raw & upd::nth_bit != 0); + auto sign = ((raw & upd::nth_bit) != 0); auto abs = (raw & upd::nth_bit) ? ~raw + 1 : raw; return sign ? -abs : abs; diff --git a/include/upd/description.hpp b/include/upd/description.hpp index 7177d2e..93ab848 100644 --- a/include/upd/description.hpp +++ b/include/upd/description.hpp @@ -70,16 +70,43 @@ class iterator_reference { namespace upd { +template +class transformer_iterator { +public: + constexpr explicit transformer_iterator(Iter iter, F f) noexcept: + m_iter{iter}, + m_f{std::move(f)} + {} + + [[nodiscard]] constexpr auto operator*() -> decltype(auto) { + return m_f(*m_iter); + } + + [[nodiscard]] constexpr auto operator++() -> transformer_iterator & { + ++m_iter; + return *this; + } + +private: + Iter m_iter; + F m_f; +}; + enum class error { none, + checksum_mismatch, }; -template +template class unexpected { public: - constexpr explicit unexpected(E e) noexcept : m_error{std::move(e)} {} + constexpr unexpected() noexcept = default; + + constexpr unexpected(E e) noexcept : m_error{std::move(e)} {} + + [[nodiscard]] constexpr operator bool() const noexcept { return static_cast(m_error); } - [[nodiscard]] constexpr auto error() noexcept -> E { return m_error; } + [[nodiscard]] constexpr auto error() const noexcept -> E { return m_error; } private: E m_error; @@ -160,12 +187,19 @@ class expected { namespace upd::descriptor { +struct all_fields_t {}; + +constexpr auto all_fields = all_fields_t{}; + template constexpr auto field(signedness_t, width_t) noexcept(release); template constexpr auto constant(T, width_t) noexcept(release); +template +[[nodiscard]] constexpr auto checksum(BinaryOp, width_t, Init, all_fields_t) noexcept(release); + } // namespace upd::descriptor namespace upd { @@ -184,6 +218,7 @@ template enum class field_tag { pure_field, constant, + checksum, }; template @@ -197,7 +232,13 @@ class description { template friend constexpr auto descriptor::constant(T, width_t) noexcept(release); + template + friend constexpr auto descriptor::checksum(BinaryOp, width_t, Init, descriptor::all_fields_t) noexcept(release); + public: + constexpr static auto identifiers = typelist{} + .transform_const([](auto field_type) { return field_type->identifier; }); + template [[nodiscard]] constexpr auto instantiate(NamedValues... nvs) const noexcept { static_assert((detail::is_instance_of_v && ...), @@ -212,6 +253,8 @@ class description { return kw = representation{value}; } else if constexpr (field.tag == field_tag::constant) { return kw = field.value; + } else if constexpr (field.tag == field_tag::checksum) { + return kw = field.value; } else { static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object"); } @@ -224,21 +267,49 @@ class description { template [[nodiscard]] constexpr auto decode(InputIt src, Serializer &ser) const noexcept(release) { - auto impl = [&](const auto &field) { + auto err = unexpected{}; + auto accumulators = m_fields + .filter([](auto field_type) { return field_type->tag == field_tag::checksum; }) + .apply([](auto &&... checksum_fields) { return name_tuple((kw = checksum_fields.init)...); }); + + auto first_pass = [&](const auto &field) { + if constexpr (field.tag == field_tag::checksum) { + auto &[op, init, get_range] = field; + constexpr auto identifier = field.identifier; + constexpr auto range = get_range(identifiers); + auto accumulate_and_pass = [&acc = accumulators[kw], op = op](auto identifier, const auto &value) mutable { + if constexpr (range.contains(identifier)) { + acc = op(acc, value); + } + return value; + }; + + return [f = std::move(accumulate_and_pass)](auto iter) { return transformer_iterator{iter, std::move(f)}; }; + } + }; + + auto decorated_src = m_fields.transform(first_pass).fold_right(src, invoke); + auto second_pass = [&](const auto &field) { + constexpr auto identifier = field.identifier; if constexpr (field.tag == field_tag::pure_field) { constexpr auto width = field.width; using representation = std::conditional_t, xuint>; - return kw = deserialize_into_xinteger(detail::iterator_reference{src}, ser); + return kw = deserialize_into_xinteger(detail::iterator_reference{src}, ser); } else if constexpr (field.tag == field_tag::constant) { - return kw = field.value; + return kw = field.value; + } else if constexpr (field.tag == field_tag::checksum) { + constexpr auto width = field.width; + using representation = std::conditional_t, xuint>; + auto received = deserialize_into_xinteger(detail::iterator_reference{src}, ser); + if (err && accumulators[kw] != received) { + err = error::checksum_mismatch; + } } else { - static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object"); + static_assert(UPD_ALWAYS_FALSE, "`field.tag` is not a valid `field_tag` enumerator"); } }; - auto make_named_tuple = [](auto &&...nvs) { return (nvs, ...); }; - - auto retval = m_fields.transform(impl).apply(make_named_tuple); + auto retval = m_fields.transform(second_pass).apply(name_tuple); return expected{std::move(retval)}; } @@ -301,6 +372,35 @@ template return description{tuple{retval}}; } +template +struct checksum_t { + constexpr static auto tag = field_tag::checksum; + constexpr static auto identifier = Identifier; + constexpr static auto is_signed = false; + constexpr static auto width = Width; + + BinaryOp op; + Init init; + GetRange get_range; +}; + +template +[[nodiscard]] constexpr auto checksum(BinaryOp op, width_t, Init init, all_fields_t) noexcept(release) { + constexpr auto get_identifier = [](auto field_type) { return field_type->identifier; }; + constexpr auto is_not_checksum_identifier = [](auto identifier) { return identifier != Identifier; }; + + auto get_range = [](const auto &fields) { + return fields + .type_only() + .transform(get_identifier) + .filter_const(is_not_checksum_identifier); + }; + auto retval = checksum_t{std::move(op), init, get_range}; + + return description{tuple{retval}}; +} + } // namespace upd::descriptor namespace upd::literals { diff --git a/include/upd/integer.hpp b/include/upd/integer.hpp index d631393..4d20011 100644 --- a/include/upd/integer.hpp +++ b/include/upd/integer.hpp @@ -98,8 +98,15 @@ class extended_integer { constexpr extended_integer() noexcept = default; template - constexpr extended_integer(T n) noexcept(release) : m_value{(underlying)reduce_scalar(n, bitsize)} { - static_assert(std::is_integral_v || std::is_enum_v, "`n` must be an integer or an enumerator"); + constexpr extended_integer(T n) noexcept(release) { + + if constexpr (std::is_integral_v || std::is_enum_v) { + m_value = (underlying) reduce_scalar(n, bitsize); + } else if constexpr (is_extended_integer_v) { + m_value = n.m_value; + } else { + static_assert(UPD_ALWAYS_FALSE, "`n` must be an integer, an enumerator, or an extended integer"); + } } template || std::is_enum_v>> @@ -179,7 +186,7 @@ class extended_integer { template [[nodiscard]] constexpr auto operator+(T rhs) const noexcept { - constexpr auto xrhs = to_extended_integer(rhs); + auto xrhs = to_extended_integer(rhs); constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize); auto retval = m_value + xrhs.m_value; @@ -188,6 +195,50 @@ class extended_integer { return xinteger{retval}; } + template + [[nodiscard]] constexpr auto operator&(T rhs) const noexcept { + auto xrhs = to_extended_integer(rhs); + constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize); + + auto retval = m_value & xrhs.m_value; + using retval_type = decltype(retval); + + return xinteger{retval}; + } + + template + [[nodiscard]] constexpr auto operator^(T rhs) const noexcept { + auto xrhs = to_extended_integer(rhs); + constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize); + + auto retval = m_value ^ xrhs.m_value; + using retval_type = decltype(retval); + + return xinteger{retval}; + } + + template + [[nodiscard]] constexpr auto operator<<(T rhs) const noexcept { + auto xrhs = to_extended_integer(rhs); + constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize); + + auto retval = m_value << xrhs.m_value; + using retval_type = decltype(retval); + + return xinteger{retval}; + } + + template + [[nodiscard]] constexpr auto operator>>(T rhs) const noexcept { + auto xrhs = to_extended_integer(rhs); + constexpr auto retval_bitsize = std::max(bitsize, xrhs.bitsize); + + auto retval = m_value >> xrhs.m_value; + using retval_type = decltype(retval); + + return xinteger{retval}; + } + [[nodiscard]] constexpr auto operator~() const noexcept { static_assert(!is_signed, "Bitwise operators only work on unsigned values"); diff --git a/include/upd/tuple.hpp b/include/upd/tuple.hpp index 8b44ca0..f0c3633 100644 --- a/include/upd/tuple.hpp +++ b/include/upd/tuple.hpp @@ -1,32 +1,52 @@ #pragma once +#include #include "detail/variadic/at.hpp" #include "detail/variadic/leaf.hpp" +namespace upd::detail { + +template +[[nodiscard]] constexpr auto apply_on_index_sequence(F &&f, std::index_sequence) -> decltype(auto) { + return UPD_FWD(f)(std::integral_constant{}...); +} + +} // namespace upd::detail + namespace upd { struct encapsulate_t {}; constexpr auto encapsulate = encapsulate_t{}; -template -struct operator_invoker { - constexpr static auto op = Operator{}; +constexpr inline auto equal_to = [](auto &&lhs, auto &&rhs) { + return UPD_FWD(lhs) == UPD_FWD(rhs); +}; - template - [[nodiscard]] constexpr auto operator()(const Lhs &lhs, const Rhs &rhs) const -> decltype(auto) { - return op(lhs, rhs); - } +constexpr inline auto plus = [](auto &&lhs, auto &&rhs) { + return UPD_FWD(lhs) + UPD_FWD(rhs); +}; - template - [[nodiscard]] constexpr auto operator()(const std::pair &lhs_rhs) const -> decltype(auto) { - const auto &[lhs, rhs] = lhs_rhs; - return op(lhs, rhs); - } +constexpr inline auto invoke = [](auto &&f, auto && ...args) { + return UPD_FWD(f)(UPD_FWD(args)...); }; -constexpr inline auto equal_to = operator_invoker>{}; -constexpr inline auto plus = operator_invoker>{}; +struct preserve_value_category_t {}; + +constexpr auto preserve_value_category = preserve_value_category_t{}; + +struct unpack_t {}; + +template +[[nodiscard]] constexpr auto operator|(unpack_t, F &&f) { + auto impl = [&](auto &&t) { + return apply(UPD_FWD(f), UPD_FWD(t)); + }; + + return impl; +} + +constexpr auto unpack = unpack_t{}; } // namespace upd @@ -141,6 +161,89 @@ struct typebox { } // namespace upd +namespace upd { + +template +constexpr auto sequence = detail::apply_on_index_sequence( + [](auto... is) { return indexlist{}; }, + std::make_index_sequence{}); + +using false_type = auto_constant; +using true_type = auto_constant; + +template +struct has_type_member : false_type {}; + +template +struct has_type_member> : true_type {}; + +template +constexpr auto has_type_member_v = has_type_member::value; + +template +struct has_value_member : false_type {}; + +template +struct has_value_member : true_type {}; + +template +constexpr auto has_value_member_v = has_value_member::value; + +template +constexpr auto constant_value_or = []() { + if constexpr (has_value_member_v) { + return Value::value; + } else { + return Default; + } +}(); + +} // namespace upd + +namespace upd { + +template +[[nodiscard]] constexpr auto get(Tuple &&) noexcept -> auto &&; + +} // namespace upd + +namespace upd::detail { + +template +class is_tuple_like { + constexpr static auto has_tuple_size = has_value_member_v>; + constexpr static auto tuple_size = constant_value_or, 0>; + + template + constexpr static auto has_tuple_element = has_type_member_v>; + + template + constexpr static auto is_nth_gettable(...) noexcept -> bool { + return false; + } + + template(std::declval()))> + constexpr static auto is_nth_gettable(int) noexcept -> bool { + return true; + } + +public: + constexpr static auto value = has_tuple_size && sequence + .all_of([](auto i) { return has_tuple_element && is_nth_gettable(0); }); +}; + +} // namespace upd::detail + +namespace upd { + +template +using is_tuple_like = auto_constant::value>; + +template +constexpr auto is_tuple_like_v = is_tuple_like::value; + +} // namespace upd + template struct std::tuple_size> { constexpr static auto value = sizeof...(Ts); @@ -173,11 +276,6 @@ struct std::tuple_element> { namespace upd::detail { -template -[[nodiscard]] constexpr auto apply_on_index_sequence(F &&f, std::index_sequence) -> decltype(auto) { - return UPD_FWD(f)(std::integral_constant{}...); -} - template struct leaf { [[nodiscard]] constexpr auto at(auto_constant) &noexcept -> T & { return value; } @@ -206,21 +304,21 @@ struct leaves, Ts...> : leaf... { constexpr static auto size = sizeof...(Ts); - explicit constexpr leaves() = default; + constexpr leaves() = default; - explicit constexpr leaves(Ts... xs) : leaf{UPD_FWD(xs)}... {} + template + explicit constexpr leaves(Us &&... xs) : leaf{UPD_FWD(xs)}... {} [[nodiscard]] constexpr auto at(...) const noexcept -> variadic::not_found_t { return variadic::not_found; } }; +template +leaves(Ts...) -> leaves; + } // namespace upd::detail namespace upd { -template -constexpr auto sequence = detail::apply_on_index_sequence([](auto... is) { return indexlist{}; }, - std::make_index_sequence{}); - template [[nodiscard]] constexpr auto get(Tuple &&) noexcept -> auto &&; @@ -233,14 +331,17 @@ template template constexpr void for_each(Tuple &&, F &&); +template +[[nodiscard]] constexpr auto transform(Tuple &&, F &&); + template class tuple_implementation { constexpr static auto normalize = [](auto &&...xs) noexcept { return fittest_tuple_like(UPD_FWD(xs)...); }; - [[nodiscard]] constexpr auto derived() noexcept -> Derived & { return reinterpret_cast(*this); } + [[nodiscard]] constexpr auto derived() noexcept -> Derived & { return static_cast(*this); } [[nodiscard]] constexpr auto derived() const noexcept -> const Derived & { - return reinterpret_cast(*this); + return static_cast(*this); } public: @@ -276,12 +377,18 @@ class tuple_implementation { template [[nodiscard]] constexpr auto at(I i) &&noexcept -> auto && { - return get(std::move(derived())); + return get(std::move(derived())); } template [[nodiscard]] constexpr auto at(I i) const &&noexcept -> const auto && { - return get(std::move(derived())); + return get(std::move(derived())); + } + + template + [[nodiscard]] constexpr auto all_of(UnaryPred &&p) const -> bool { + auto check = [&](auto truth, const auto &x) { return truth && p(x); }; + return fold_left(true, check); } [[nodiscard]] constexpr auto flatten() const { @@ -341,6 +448,24 @@ class tuple_implementation { return derived().apply(invoke_impl_const); } + template + [[nodiscard]] constexpr auto fold_left(Init &&init, BinaryOp &&op) const noexcept { + auto impl = [&](auto &&... xs) { + auto acc = UPD_FWD(init); + ((void)(acc = op(std::move(acc), UPD_FWD(xs))), ...); + + return acc; + }; + + return derived().apply(impl); + } + + template + [[nodiscard]] constexpr auto fold_right(Init &&init, BinaryOp &&op) const noexcept { + auto flipped_op = [&](auto &&lhs, auto &&rhs) { return op(UPD_FWD(rhs), UPD_FWD(lhs)); }; + return derived().reverse().fold_left(UPD_FWD(init), flipped_op); + } + template typename TT> [[nodiscard]] constexpr static auto metatransform() noexcept { auto apply_and_box_type = [](auto i) { @@ -353,6 +478,25 @@ class tuple_implementation { return sequence.transform(apply_and_box_type); } + [[nodiscard]] constexpr auto reverse() const { + constexpr auto rindices = []() { + auto rindices = std::array{}; + auto first = rindices.rbegin(); + auto last = rindices.rend(); + auto i = std::size_t{0}; + for (auto &ri : detail::range{first, last}) { + ri = i++; + } + + return rindices; + }(); + + auto get_element = [&](auto i) -> auto && { + return get(derived()); + }; + return sequence.transform(get_element, preserve_value_category); + } + [[nodiscard]] constexpr auto square() { auto seq = sequence; auto pair_up = [&](auto i) { return seq.transform([&](auto j) { return indexlist{}; }); }; @@ -387,6 +531,26 @@ class tuple_implementation { return transform_and_apply(std::move(derived()), UPD_FWD(f), normalize); } + template + [[nodiscard]] constexpr auto transform(F &&f, preserve_value_category_t) & { + return upd::transform(derived(), UPD_FWD(f), preserve_value_category); + } + + template + [[nodiscard]] constexpr auto transform(F &&f, preserve_value_category_t) const & { + return upd::transform(derived(), UPD_FWD(f), preserve_value_category); + } + + template + [[nodiscard]] constexpr auto transform(F &&f, preserve_value_category_t) && { + return upd::transform(std::move(derived()), UPD_FWD(f), preserve_value_category); + } + + template + [[nodiscard]] constexpr auto transform(F &&f, preserve_value_category_t) const && { + return upd::transform(std::move(derived()), UPD_FWD(f), preserve_value_category); + } + template [[nodiscard]] constexpr auto transform_const(F f) const noexcept { auto apply_const = [&](auto x) { return auto_constant{}; }; @@ -439,12 +603,21 @@ class tuple_implementation { [[nodiscard]] constexpr auto apply_const(F f, encapsulate_t) const noexcept { auto impl = [&](auto... xs) { constexpr auto result = f(xs...); - return [&] { return result; }; + return [result] { return result; }; }; return derived().apply(impl); } + template typename TT, typename... Args> + [[nodiscard]] constexpr auto apply_template(Args &&... args) const noexcept { + auto instantiate_and_construct = [&](auto... types) { + return TT{UPD_FWD(args)...}; + }; + + return derived().type_only().apply(instantiate_and_construct); + } + template constexpr void for_each(F &&f) & { upd::for_each(derived(), UPD_FWD(f)); @@ -576,7 +749,6 @@ template constexpr auto seq = std::make_index_sequence{}; auto invoke_f_once = [&](auto i) -> decltype(auto) { return f(get(UPD_FWD(t))); }; - auto invoke_f_on_each = [&](auto... is) -> decltype(auto) { return UPD_FWD(agg)(invoke_f_once(is)...); }; return detail::apply_on_index_sequence(invoke_f_on_each, seq); @@ -599,4 +771,13 @@ constexpr void for_each(Tuple &&t, F &&f) { apply(UPD_FWD(t), impl); } +template +[[nodiscard]] constexpr auto transform(Tuple &&t, F &&f, preserve_value_category_t) { + auto impl = [&](auto &&...xs) { + return tuple{f(UPD_FWD(xs))...}; + }; + + apply(UPD_FWD(t), impl); +} + } // namespace upd