diff --git a/example/dynamixel-protocol-2.0.cpp b/example/dynamixel-protocol-2.0.cpp index e1f60d9..58cce1f 100644 --- a/example/dynamixel-protocol-2.0.cpp +++ b/example/dynamixel-protocol-2.0.cpp @@ -158,7 +158,7 @@ int main() { std::cout << std::hex; auto ping_ex1 = description.instantiate(ser, - upd::kw<"id"> = 1, upd::kw<"length"> = 3, upd::kw<"instruction"> = 1); + "id"_kw = 1, "length"_kw = 3, "instruction"_kw = 1); if (!ping_ex1) { return (int)ping_ex1.error(); } @@ -174,12 +174,12 @@ int main() { } std::printf("Answer 1: \n"); - std::printf("- id: %" PRIx8 "\n", (std::uint8_t)(*answer1)[upd::kw<"id">]); - std::printf("- length: %" PRIx16 "\n", (std::uint16_t)(*answer1)[upd::kw<"length">]); - std::printf("- instruction: %" PRIx8 "\n", (std::uint8_t)(*answer1)[upd::kw<"instruction">]); - std::printf("- error: %" PRIx8 "\n", (std::uint8_t)(*answer1)[upd::kw<"error">]); - std::printf("- model number: %" PRIx16 "\n", (std::uint16_t)(*answer1)[upd::kw<"model_number">]); - std::printf("- firmware version: %" PRIx8 "\n", (std::uint8_t)(*answer1)[upd::kw<"firmware_version">]); + std::printf("- id: %" PRIx8 "\n", (std::uint8_t)(*answer1)["id"_kw]); + std::printf("- length: %" PRIx16 "\n", (std::uint16_t)(*answer1)["length"_kw]); + std::printf("- instruction: %" PRIx8 "\n", (std::uint8_t)(*answer1)["instruction"_kw]); + std::printf("- error: %" PRIx8 "\n", (std::uint8_t)(*answer1)["error"_kw]); + std::printf("- model number: %" PRIx16 "\n", (std::uint16_t)(*answer1)["model_number"_kw]); + std::printf("- firmware version: %" PRIx8 "\n", (std::uint8_t)(*answer1)["firmware_version"_kw]); std::printf("\n\n"); auto answer2_seq = bytearray{0xff, 0xff, 0xfd, 0x00, 0x02, 0x07, 0x00, 0x55, 0x00, 0x06, 0x04, 0x26, 0x6f, 0x6d}; @@ -189,11 +189,11 @@ int main() { } std::printf("Answer 2: \n"); - std::printf("- id: %" PRIx8 "\n", (std::uint8_t)(*answer2)[upd::kw<"id">]); - std::printf("- length: %" PRIx16 "\n", (std::uint16_t)(*answer2)[upd::kw<"length">]); - std::printf("- instruction: %" PRIx8 "\n", (std::uint8_t)(*answer2)[upd::kw<"instruction">]); - std::printf("- error: %" PRIx8 "\n", (std::uint8_t)(*answer2)[upd::kw<"error">]); - std::printf("- model number: %" PRIx16 "\n", (std::uint16_t)(*answer2)[upd::kw<"model_number">]); - std::printf("- firmware version: %" PRIx8 "\n", (std::uint8_t)(*answer2)[upd::kw<"firmware_version">]); + std::printf("- id: %" PRIx8 "\n", (std::uint8_t)(*answer2)["id"_kw]); + std::printf("- length: %" PRIx16 "\n", (std::uint16_t)(*answer2)["length"_kw]); + std::printf("- instruction: %" PRIx8 "\n", (std::uint8_t)(*answer2)["instruction"_kw]); + std::printf("- error: %" PRIx8 "\n", (std::uint8_t)(*answer2)["error"_kw]); + std::printf("- model number: %" PRIx16 "\n", (std::uint16_t)(*answer2)["model_number"_kw]); + std::printf("- firmware version: %" PRIx8 "\n", (std::uint8_t)(*answer2)["firmware_version"_kw]); std::printf("\n\n"); } diff --git a/include/upd/description.hpp b/include/upd/description.hpp index f7a869b..a1f94b4 100644 --- a/include/upd/description.hpp +++ b/include/upd/description.hpp @@ -321,13 +321,13 @@ class description { if constexpr (field.tag == field_tag::pure_field) { constexpr auto width = field.width; using representation = std::conditional_t, xuint>; - auto value = named_args[kw]; - return kw = representation{value}; + auto value = named_args[keyword{}]; + return keyword{} = representation{value}; } else if constexpr (field.tag == field_tag::constant) { - return kw = field.value; + return keyword{} = field.value; } else if constexpr (field.tag == field_tag::checksum) { constexpr auto width = field.width; - return kw = xuint{field.init}; + return keyword{} = xuint{field.init}; } else { static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object"); } @@ -339,13 +339,13 @@ class description { if constexpr (field.tag == field_tag::checksum) { constexpr auto identifier = field.identifier; auto &[op, init, get_range] = field; - auto &acc = retval[kw]; + auto &acc = retval[keyword{}]; auto folder = invoker_iterator{ [&, op=op](auto x) { acc = op(acc, x); } }; get_range(identifiers).for_each([&](auto id) { - return retval[kw].serialize(ser, folder); + return retval[keyword{}].serialize(ser, folder); }); } }; @@ -367,7 +367,7 @@ class description { auto first_pass = [&](const auto &field) { if constexpr (field.tag == field_tag::checksum) { const auto &[op, init, get_range] = field; - auto &acc = accumulators[kw]; + auto &acc = accumulators[keyword{}]; auto range = get_range(identifiers); auto accumulate = [&, &op = op](auto identifier, const auto &value) { constexpr auto p = [=](auto id_type) { return auto_constantvalue == identifier>{}; }; @@ -393,7 +393,7 @@ class description { return value; } }; - return kw = deserialize_into_xinteger(decorated_src, ser); + return keyword{} = deserialize_into_xinteger(decorated_src, ser); } else if constexpr (field.tag == field_tag::constant) { constexpr auto width = field.width; using representation = std::conditional_t, xuint>; @@ -413,7 +413,7 @@ class description { 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) { + if (!err && accumulators[keyword{}] != received) { err = error::checksum_mismatch; } } else { diff --git a/include/upd/detail/sequence.hpp b/include/upd/detail/sequence.hpp new file mode 100644 index 0000000..2717957 --- /dev/null +++ b/include/upd/detail/sequence.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace upd::detail { + +template +[[nodiscard]] constexpr auto apply_on_index_sequence(F &&f, std::index_sequence) -> decltype(auto) { + return std::invoke(UPD_FWD(f), std::integral_constant{}...); +} + +} // namespace upd::detail \ No newline at end of file diff --git a/include/upd/named_tuple.hpp b/include/upd/named_tuple.hpp index 618f971..774c5e7 100644 --- a/include/upd/named_tuple.hpp +++ b/include/upd/named_tuple.hpp @@ -1,130 +1,3 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include - -#include "description.hpp" -#include "detail/always_false.hpp" -#include "detail/integral_constant.hpp" -#include "detail/tuple_operations.hpp" -#include "detail/variadic/concat.hpp" -#include "detail/variadic/count.hpp" -#include "detail/variadic/find.hpp" -#include "integer.hpp" -#include "upd.hpp" - -namespace upd { - -template -struct name { - constexpr name(const char (&s)[N]): value{} { - std::ranges::copy(s, value); - } - - template - [[nodiscard]] constexpr auto operator==(const name &rhs) const noexcept -> bool { - return std::string_view{value} == std::string_view{rhs.value}; - }; - - char value[N]; -}; - -template -struct kw_t; - -template -[[nodiscard]] constexpr auto name_tuple(std::tuple) noexcept; - -template -[[nodiscard]] constexpr auto concat_named_tuple(Named_Tuple_Ts &&...); - -template -class named_tuple { - using id_ts = detail::integral_constant_tuple_t; - - template - friend constexpr auto name_tuple(std::tuple) noexcept; - - template - friend constexpr auto concat_named_tuple(Named_Tuple_Ts &&...); - -public: - template - [[nodiscard]] constexpr auto get() noexcept -> auto & { - using id_t = detail::integral_constant_t; - constexpr auto id_pos = detail::variadic::find_v; - - static_assert(id_pos != detail::variadic::not_found, "`Id` does not belong to `Ids`"); - - return std::get(m_content); - } - - template - [[nodiscard]] constexpr auto get() const noexcept -> const auto & { - using id_t = detail::integral_constant_t; - constexpr auto id_pos = detail::variadic::find_v; - - static_assert(id_pos != detail::variadic::not_found, "`Id` does not belong to `Ids`"); - - return std::get(m_content); - } - - template - [[nodiscard]] constexpr auto operator[](kw_t) noexcept(release) -> auto & { - return get(); - } - - template - [[nodiscard]] constexpr auto operator[](kw_t) const noexcept(release) -> const auto & { - return get(); - } - - template - constexpr void serialize(Serializer &ser, OutputIt output) { - auto serialize_pack = [&](const auto &...xs) { (xs.serialize(ser, output), ...); }; - - std::apply(serialize_pack, m_content); - } - -private: - constexpr explicit named_tuple(Tuple_T content, detail::integral_constant_tuple_t) - : m_content{UPD_FWD(content)} {} - - Tuple_T m_content; -}; - -template -struct is_named_tuple_instance : std::false_type {}; - -template -struct is_named_tuple_instance> : std::true_type {}; - -template -[[nodiscard]] constexpr auto name_tuple(std::tuple tuple) noexcept { - using id_ts = detail::integral_constant_tuple_t; - - if constexpr (sizeof...(Ids) != sizeof...(Ts)) { - static_assert(UPD_ALWAYS_FALSE, "`Ids` and `Ts` must contain the same number of elements"); - } else if constexpr (((detail::variadic::count_v> != 1) || ...)) { - static_assert(UPD_ALWAYS_FALSE, "Each element in `Ids` must be unique"); - } else { - return named_tuple{std::move(tuple), id_ts{}}; - } -} - -template -[[nodiscard]] constexpr auto concat_named_tuple(Named_Tuple_Ts &&...named_tuples) { - using id_ts = detail::variadic::concat_t; - - auto content = std::tuple_cat(UPD_FWD(named_tuples).m_content...); - auto name_content = [&](auto... id_iconsts) { return name_tuple(std::move(content)); }; - - return std::apply(name_content, id_ts{}); -} - -} // namespace upd +#include "named_value.hpp" diff --git a/include/upd/named_value.hpp b/include/upd/named_value.hpp index cf1129e..961b6a6 100644 --- a/include/upd/named_value.hpp +++ b/include/upd/named_value.hpp @@ -5,6 +5,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "description.hpp" #include "detail/always_false.hpp" @@ -12,16 +19,66 @@ #include "detail/tuple_operations.hpp" #include "detail/variadic/concat.hpp" #include "integer.hpp" -#include "named_tuple.hpp" +#include "upd.hpp" +#include "tuple.hpp" +#include "description.hpp" +#include "detail/always_false.hpp" +#include "detail/integral_constant.hpp" +#include "detail/tuple_operations.hpp" +#include "detail/variadic/concat.hpp" +#include "detail/variadic/count.hpp" +#include "detail/variadic/find.hpp" +#include "integer.hpp" #include "upd.hpp" namespace upd { -template -struct kw_t; +constexpr auto name_max_size = std::size_t{256}; -template -constexpr auto kw = kw_t{}; +template +concept xinteger_instance = requires(T x) { + { extended_integer{x} } -> std::same_as; +}; + +template +concept serializer = requires(T x, xuint<2 * bitsize_v> xui, xint<2 * bitsize_v> xi, Byte *it) { + { x.serialize_unsigned(xui, it) } -> std::same_as; + { x.serialize_signed(xi, it) } -> std::same_as; + { x.deserialize_unsigned(std::as_const(it), width) } -> xinteger_instance; + { x.deserialize_signed(std::as_const(it), width) } -> xinteger_instance; +}; + +template +struct keyword; + +struct name { + template + consteval name(const char (&str)[Size]) noexcept(release): string{} { + using namespace std::ranges; + + copy(str, string); + } + + char string[name_max_size]; +}; + +[[nodiscard]] consteval inline auto operator==(const name &lhs, const name &rhs) noexcept(release) -> bool { + return std::string_view{lhs.string} == std::string_view{rhs.string}; +}; + +template +struct names { + consteval names(const std::array &ns) noexcept(release): strings{} { + using namespace std::ranges; + using namespace std::ranges::views; + + for (auto i : iota(0u, N)) { + copy(ns.at(i).string, strings[i++]); + } + } + + char strings[N][name_max_size]; +}; template class named_value { @@ -34,19 +91,19 @@ class named_value { template [[nodiscard]] constexpr auto map(F &&f) & { decltype(auto) mapped_value = std::invoke(UPD_FWD(f), m_value); - return kw = UPD_FWD(mapped_value); + return keyword{} = UPD_FWD(mapped_value); } template [[nodiscard]] constexpr auto map(F &&f) const & { decltype(auto) mapped_value = std::invoke(UPD_FWD(f), m_value); - return kw = UPD_FWD(mapped_value); + return keyword{} = UPD_FWD(mapped_value); } template [[nodiscard]] constexpr auto map(F &&f) && { decltype(auto) mapped_value = std::invoke(UPD_FWD(f), std::move(m_value)); - return kw = UPD_FWD(mapped_value); + return keyword{} = UPD_FWD(mapped_value); } [[nodiscard]] constexpr auto value() const & noexcept -> const T& { @@ -57,46 +114,111 @@ class named_value { return std::move(m_value); } - template - [[nodiscard]] constexpr auto operator,(NamedObject nobj) const & { - if constexpr (requires { upd::named_value{nobj}; }) { - auto values = std::tuple{m_value, std::move(nobj).value()}; - return name_tuple(std::move(values)); - } else if constexpr (is_named_tuple_instance::value) { - auto tail = std::tuple{m_value}; - auto named_tail = name_tuple(std::move(tail)); - return concat_named_tuple(std::move(named_tail), std::move(nobj)); - } else { - static_assert(UPD_ALWAYS_FALSE, "`nobj` must be a `named_value` or `named_tuple` instance"); - } +private: + T m_value; +}; + +template +concept named_value_instance = requires(T x) { + { named_value{x} } -> std::same_as; +}; + +template +[[nodiscard]] constexpr auto name_tuple(std::tuple) noexcept; + +template +[[nodiscard]] constexpr auto concat_named_tuple(Named_Tuple_Ts &&...); + +template +requires (std::size(Identifiers.strings) == sizeof...(Ts)) +class named_tuple { + using content_type = decltype(sequence + .apply([](auto... is) { + constexpr auto &identifiers = Identifiers.strings; + return tuple{(keyword{} = std::declval())...}; + })); + +public: + template requires (sizeof...(NamedValues) == sizeof...(Ts)) + constexpr named_tuple(NamedValues && ...nvs): m_nvs{UPD_FWD(nvs)...} {} + + template + [[nodiscard]] constexpr auto get() noexcept -> auto & { + auto maybe_element = m_nvs + .find_const([](auto nv_type) { return nv_type->identifier == Identifier; }); + + return *maybe_element; } - template - [[nodiscard]] constexpr auto operator,(NamedObject nobj) &&noexcept { - if constexpr (requires { upd::named_value{nobj}; }) { - auto values = std::tuple{std::move(m_value), std::move(nobj).value()}; - return name_tuple(std::move(values)); - } else if constexpr (is_named_tuple_instance::value) { - auto tail = std::tuple{std::move(m_value)}; - auto named_tail = name_tuple(std::move(tail)); - return concat_named_tuple(std::move(named_tail), std::move(nobj)); - } else { - static_assert(UPD_ALWAYS_FALSE, "`nobj` must be a `named_value` or `named_tuple` instance"); - } + template + [[nodiscard]] constexpr auto get() const noexcept -> const auto & { + auto maybe_element = m_nvs + .find_const([](auto nv_type) { return nv_type->identifier == Identifier; }); + + return *maybe_element; + } + + template + [[nodiscard]] constexpr auto operator[](keyword) noexcept(release) -> auto & { + return get(); + } + + template + [[nodiscard]] constexpr auto operator[](keyword) const noexcept(release) -> const auto & { + return get(); + } + + template OutputIt> + constexpr void serialize(Serializer &ser, OutputIt output) const { + auto serialize_pack = [&](const auto &...xs) { (xs.serialize(ser, output), ...); }; + m_nvs.apply(serialize_pack); } private: - T m_value; + content_type m_nvs; +}; + +template +concept named_tuple_instance = requires(T x) { + { named_tuple{x} } -> std::same_as; }; -template -struct kw_t { + +template +[[nodiscard]] constexpr auto concat(NamedTuples &&...nts) { + return tuple{nts...} + .flatten() + .apply([](auto &&... nvs) { return named_tuple{UPD_FWD(nvs)...}; }); +} + +template +[[nodiscard]] constexpr auto operator,(Lhs &&lhs, Rhs &&rhs) noexcept(release) { + return named_tuple{UPD_FWD(lhs), UPD_FWD(rhs)}; +} + +template +[[nodiscard]] constexpr auto operator,(Lhs &&lhs, Rhs &&rhs) noexcept(release) { + auto singleton = named_tuple{UPD_FWD(rhs)}; + return concat(UPD_FWD(lhs), std::move(singleton)); +} + +template +struct keyword { constexpr static auto identifier = Identifier; template - [[nodiscard]] constexpr auto operator=(T x) const noexcept -> named_value { + [[nodiscard]] constexpr auto operator=(T x) const noexcept(release) -> named_value { return named_value{std::in_place, UPD_FWD(x)}; } }; } // namespace upd + +namespace upd::literals { + +template +[[nodiscard]] constexpr auto operator""_kw() noexcept(release) { + return keyword{}; +} + +} // namespace upd::literals diff --git a/include/upd/tuple.hpp b/include/upd/tuple.hpp index fda7e78..d6dea6e 100644 --- a/include/upd/tuple.hpp +++ b/include/upd/tuple.hpp @@ -9,15 +9,6 @@ #include "detail/variadic/clean.hpp" #include "detail/is_instance_of.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 { template typename TT> @@ -52,7 +43,7 @@ struct filter_void_t {}; constexpr auto filter_void = filter_void_t{}; template -[[nodiscard]] constexpr auto apply(Tuple &&, F &&); +[[nodiscard]] constexpr auto apply(Tuple &&, F &&) -> decltype(auto); struct encapsulate_t {}; @@ -205,9 +196,9 @@ struct typebox { namespace upd { template -constexpr auto sequence = detail::apply_on_index_sequence( - [](auto... is) { return indexlist{}; }, - std::make_index_sequence{}); +constinit auto sequence = [](std::index_sequence) { + return indexlist{}; +}(std::make_index_sequence{}); using false_type = auto_constant; using true_type = auto_constant; @@ -283,6 +274,19 @@ using is_tuple_like = auto_constant::value>; template constexpr auto is_tuple_like_v = is_tuple_like::value; +template +concept nth_gettable = requires(T &x) { + typename std::tuple_element_t; + { get(x) } -> std::same_as &>; + { get(std::move(x)) } -> std::same_as &&>; +}; + +template +concept tuple_like = requires(T &x) { + { std::tuple_size_v } -> std::same_as; + +}; + } // namespace upd template @@ -816,6 +820,10 @@ class constlist : public tuple_implementation> { leaves m_leaves; }; +template +constexpr auto as_constlist = sequence> + .transform_const([](auto i) { return std::get(TupleLike); }); + template [[nodiscard]] constexpr auto fittest_tuple_like(Ts &&...xs) { if constexpr ((detail::is_instance_of_v, typebox> && ...)) { @@ -852,25 +860,26 @@ template return retval; } -template -[[nodiscard]] constexpr auto transform_and_apply(Tuple &&t, F &&f, Aggregator &&agg) { - constexpr auto size = std::tuple_size_v>; - 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)...); }; +template +constexpr auto transform_and_apply(Tuple &&t, F &&f, Aggregator &&agg) -> decltype(auto) { + auto impl = [&](auto... is) -> decltype(auto) { + return std::invoke( + UPD_FWD(agg), + std::invoke(f, get(UPD_FWD(t)))... + ); + }; - return detail::apply_on_index_sequence(invoke_f_on_each, seq); + apply(UPD_FWD(t), impl); } -template -[[nodiscard]] constexpr auto apply(Tuple &&t, F &&f) { +template +constexpr auto apply(Tuple &&t, F &&f) -> decltype(auto) { constexpr auto size = std::tuple_size_v>; constexpr auto seq = std::make_index_sequence{}; - auto apply_f = [&](auto... is) -> decltype(auto) { return UPD_FWD(f)(get(UPD_FWD(t))...); }; - - return detail::apply_on_index_sequence(apply_f, seq); + return [&](std::index_sequence) -> decltype(auto) { + return std::invoke(UPD_FWD(f), get(UPD_FWD(t))...); + }(seq); } template