Skip to content

Commit

Permalink
Overhaul keywords and identifier implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
StarQTius committed Oct 4, 2024
1 parent 2f4ca46 commit a9698f7
Show file tree
Hide file tree
Showing 7 changed files with 517 additions and 324 deletions.
32 changes: 17 additions & 15 deletions example/dynamixel-protocol-2.0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ struct serializer {

template<typename InputIt, std::size_t Bitsize>
auto deserialize_signed(InputIt input, upd::width_t<Bitsize>) {
using namespace upd::literals;

static_assert((Bitsize + 1) % bytewidth == 0, "`Bitsize` must be a multiple of `bytewidth`");

constexpr auto size = (Bitsize + 1) / bytewidth;
Expand All @@ -135,9 +137,9 @@ struct serializer {

auto raw = upd::recompose_into_xuint(byteseq);
auto sign = ((raw & upd::nth_bit<Bitsize>) != 0);
auto abs = (raw & upd::nth_bit<Bitsize>) ? ~raw + 1 : raw;
auto abs = (raw & upd::nth_bit<Bitsize>) ? ~raw + 1_xui : raw;

return sign ? -abs : abs;
return sign ? -abs : abs.as_signed();
}
};

Expand All @@ -158,7 +160,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();
}
Expand All @@ -174,12 +176,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};
Expand All @@ -189,11 +191,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");
}
41 changes: 19 additions & 22 deletions include/upd/description.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,6 @@ class iterator_reference {

namespace upd {

template<typename T, template<typename...> typename TT>
concept instance_of = detail::is_instance_of_v<T, TT>;

template<typename>
class invoker_iterator;

Expand Down Expand Up @@ -311,41 +308,41 @@ class description {
constexpr static auto identifiers = typelist<Ts...>{}
.transform_const([](auto field_type) { return field_type->identifier; });

template<typename Serializer, typename... NamedValues>
[[nodiscard]] constexpr auto instantiate(Serializer &ser, NamedValues... nvs) const noexcept {
template<typename Serializer, named_value_instance... NamedValues>
[[nodiscard]] constexpr auto instantiate(Serializer &ser, NamedValues &&... nvs) const noexcept {
static_assert((requires { named_value{nvs}; } && ...),
"`nvs` must be a pack of `named_value` instances");

auto named_args = (nvs, ...);
auto named_args = named_tuple{UPD_FWD(nvs)...};
auto first_pass = [&](const auto &field) {
if constexpr (field.tag == field_tag::pure_field) {
constexpr auto width = field.width;
using representation = std::conditional_t<field.is_signed, xint<width>, xuint<width>>;
auto value = named_args[kw<field.identifier>];
return kw<field.identifier> = representation{value};
auto value = named_args[keyword<field.identifier>{}];
return keyword<field.identifier>{} = representation{value};
} else if constexpr (field.tag == field_tag::constant) {
return kw<field.identifier> = field.value;
return keyword<field.identifier>{} = field.value;
} else if constexpr (field.tag == field_tag::checksum) {
constexpr auto width = field.width;
return kw<field.identifier> = xuint<width>{field.init};
return keyword<field.identifier>{} = xuint<width>{field.init};
} else {
static_assert(UPD_ALWAYS_FALSE, "`field` is not a field object");
}
};

auto make_named_tuple = [](auto &&...nvs) { return name_tuple<nvs.identifier...>(std::tuple{UPD_FWD(nvs).value()...}); };
auto make_named_tuple = [](auto &&...nvs) { return named_tuple{UPD_FWD(nvs)...}; };
auto retval = m_fields.transform(first_pass).apply(make_named_tuple);
auto second_pass = [&](const auto &field) {
if constexpr (field.tag == field_tag::checksum) {
constexpr auto identifier = field.identifier;
auto &[op, init, get_range] = field;
auto &acc = retval[kw<identifier>];
auto &acc = retval[keyword<identifier>{}];
auto folder = invoker_iterator{
[&, op=op](auto x) { acc = op(acc, x); }
};

get_range(identifiers).for_each([&](auto id) {
return retval[kw<id.value>].serialize(ser, folder);
return retval[keyword<id.value>{}].serialize(ser, folder);
});
}
};
Expand All @@ -358,19 +355,19 @@ class description {
[[nodiscard]] constexpr auto decode(InputIt src, Serializer &ser) const noexcept(release) {
auto err = unexpected{};
auto accumulators = m_fields
.filter([](auto field_type) { return field_type->tag == field_tag::checksum; })
.filter([](const auto &field) { return expr<field.tag == field_tag::checksum>; })
.apply([](auto &&... checksum_fields) {
return name_tuple<checksum_fields.identifier...>(std::tuple{checksum_fields.init...});
return named_tuple { (keyword<checksum_fields.identifier>{} = checksum_fields.init)...};
}
);

auto first_pass = [&](const auto &field) {
if constexpr (field.tag == field_tag::checksum) {
const auto &[op, init, get_range] = field;
auto &acc = accumulators[kw<field.identifier>];
auto &acc = accumulators[keyword<field.identifier>{}];
auto range = get_range(identifiers);
auto accumulate = [&, &op = op](auto identifier, const auto &value) {
constexpr auto p = [=](auto id_type) { return auto_constant<id_type->value == identifier>{}; };
constexpr auto p = [=](const auto &id) { return auto_constant<id.value == identifier>{}; };
if constexpr (range.filter(p).size() > 0) {
acc = op(acc, value);
}
Expand All @@ -393,7 +390,7 @@ class description {
return value;
}
};
return kw<identifier> = deserialize_into_xinteger<representation>(decorated_src, ser);
return keyword<identifier>{} = deserialize_into_xinteger<representation>(decorated_src, ser);
} else if constexpr (field.tag == field_tag::constant) {
constexpr auto width = field.width;
using representation = std::conditional_t<field.is_signed, xint<width>, xuint<width>>;
Expand All @@ -413,7 +410,7 @@ class description {
constexpr auto width = field.width;
using representation = std::conditional_t<field.is_signed, xint<width>, xuint<width>>;
auto received = deserialize_into_xinteger<representation>(detail::iterator_reference{src}, ser);
if (!err && accumulators[kw<identifier>] != received) {
if (!err && accumulators[keyword<identifier>{}] != received) {
err = error::checksum_mismatch;
}
} else {
Expand All @@ -423,7 +420,7 @@ class description {

auto retval = m_fields
.transform(second_pass, filter_void)
.apply([](auto &&... nvs) { return name_tuple<nvs.identifier...>(std::tuple{UPD_FWD(nvs).value()...}); });
.apply([](auto &&... nvs) { return named_tuple{UPD_FWD(nvs)...}; });
return err ? err : expected{std::move(retval)};
}

Expand All @@ -444,7 +441,7 @@ template<typename... Ts, typename... Us>
auto self_comparison_count = fields.type_only()
.transform_const(get_identifier)
.square()
.transform(unpack | equal_to)
.transform(unpack | [](auto lhs, auto rhs) { return expr<lhs == rhs>; })
.fold_const(auto_constant<std::size_t{0}>{}, plus);

static_assert(self_comparison_count == fields.size(), "Merging these descriptions would result in duplicate IDs");
Expand Down Expand Up @@ -500,7 +497,7 @@ struct checksum_t {

template<name Identifier, typename BinaryOp, std::size_t Width, typename Init>
[[nodiscard]] constexpr auto checksum(BinaryOp op, width_t<Width>, Init init, all_fields_t) noexcept(release) {
auto get_range = [](auto identifiers) { return identifiers.filter([](auto id_type) { return id_type->value != Identifier; }); };
auto get_range = [](auto identifiers) { return identifiers.filter([](auto id) { return auto_constant<id != Identifier>{}; }); };
auto retval = checksum_t<Identifier, BinaryOp, Width,
Init, decltype(get_range)>{std::move(op), init, get_range};

Expand Down
12 changes: 12 additions & 0 deletions include/upd/detail/sequence.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <type_traits>

namespace upd::detail {

template<typename F, std::size_t... Is>
[[nodiscard]] constexpr auto apply_on_index_sequence(F &&f, std::index_sequence<Is...>) -> decltype(auto) {
return std::invoke(UPD_FWD(f), std::integral_constant<std::size_t, Is>{}...);
}

} // namespace upd::detail
29 changes: 29 additions & 0 deletions include/upd/integer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ class extended_integer {
}
}

[[nodiscard]] constexpr operator bool() const noexcept(release) {
return static_cast<bool>(m_value);
}

template<typename T, typename = std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>>
constexpr operator T() const noexcept {
if constexpr (std::is_integral_v<T>) {
Expand Down Expand Up @@ -136,6 +140,10 @@ class extended_integer {
return decompose_into_xuint<byte>(*this);
}

[[nodiscard]] constexpr auto as_signed() const noexcept(release) {
return xinteger<bitsize, std::make_signed_t<underlying>>{m_value};
}

[[nodiscard]] constexpr auto abs() const noexcept {
if constexpr (std::is_unsigned_v<Underlying_T>) {
return *this;
Expand Down Expand Up @@ -182,6 +190,10 @@ class extended_integer {
}
}

[[nodiscard]] constexpr auto operator-() const noexcept(release) {
return xint<bitsize>{-m_value};
}

template<typename T>
[[nodiscard]] constexpr auto operator+(T rhs) const noexcept {
auto xrhs = to_extended_integer(rhs);
Expand Down Expand Up @@ -378,3 +390,20 @@ template<typename Enum>
}

} // namespace upd

namespace upd::literals {

template<char... Cs>
[[nodiscard]] constexpr auto operator ""_xui() noexcept(release) {
constexpr auto characters = std::array {Cs...};
constexpr auto integer = (std::uintmax_t) detail::ascii_to_integer(characters.begin(), characters.end());
constexpr auto bitsize = [] {
auto shift_count = std::size_t{0};
for (auto acc = integer; acc > 0; ++shift_count, acc >>= 1);
return shift_count;
}();

return xuint<bitsize>{integer};
}

} // namespace upd::literals
129 changes: 1 addition & 128 deletions include/upd/named_tuple.hpp
Original file line number Diff line number Diff line change
@@ -1,130 +1,3 @@
#pragma once

#include <array>
#include <ranges>
#include <string>
#include <limits>
#include <tuple>
#include <type_traits>
#include <utility>

#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<std::size_t N>
struct name {
constexpr name(const char (&s)[N]): value{} {
std::ranges::copy(s, value);
}

template<std::size_t M>
[[nodiscard]] constexpr auto operator==(const name<M> &rhs) const noexcept -> bool {
return std::string_view{value} == std::string_view{rhs.value};
};

char value[N];
};

template<name>
struct kw_t;

template<name..., typename... Ts>
[[nodiscard]] constexpr auto name_tuple(std::tuple<Ts...>) noexcept;

template<typename... Named_Tuple_Ts>
[[nodiscard]] constexpr auto concat_named_tuple(Named_Tuple_Ts &&...);

template<typename Tuple_T, name... Ids>
class named_tuple {
using id_ts = detail::integral_constant_tuple_t<Ids...>;

template<name..., typename... Ts>
friend constexpr auto name_tuple(std::tuple<Ts...>) noexcept;

template<typename... Named_Tuple_Ts>
friend constexpr auto concat_named_tuple(Named_Tuple_Ts &&...);

public:
template<name Id>
[[nodiscard]] constexpr auto get() noexcept -> auto & {
using id_t = detail::integral_constant_t<Id>;
constexpr auto id_pos = detail::variadic::find_v<id_ts, id_t>;

static_assert(id_pos != detail::variadic::not_found, "`Id` does not belong to `Ids`");

return std::get<id_pos>(m_content);
}

template<name Id>
[[nodiscard]] constexpr auto get() const noexcept -> const auto & {
using id_t = detail::integral_constant_t<Id>;
constexpr auto id_pos = detail::variadic::find_v<id_ts, id_t>;

static_assert(id_pos != detail::variadic::not_found, "`Id` does not belong to `Ids`");

return std::get<id_pos>(m_content);
}

template<name Identifier>
[[nodiscard]] constexpr auto operator[](kw_t<Identifier>) noexcept(release) -> auto & {
return get<Identifier>();
}

template<name Identifier>
[[nodiscard]] constexpr auto operator[](kw_t<Identifier>) const noexcept(release) -> const auto & {
return get<Identifier>();
}

template<typename Serializer, typename OutputIt>
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<Ids...>)
: m_content{UPD_FWD(content)} {}

Tuple_T m_content;
};

template<typename>
struct is_named_tuple_instance : std::false_type {};

template<typename Tuple, name... Identifiers>
struct is_named_tuple_instance<named_tuple<Tuple, Identifiers...>> : std::true_type {};

template<name... Ids, typename... Ts>
[[nodiscard]] constexpr auto name_tuple(std::tuple<Ts...> tuple) noexcept {
using id_ts = detail::integral_constant_tuple_t<Ids...>;

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<id_ts, detail::integral_constant_t<Ids>> != 1) || ...)) {
static_assert(UPD_ALWAYS_FALSE, "Each element in `Ids` must be unique");
} else {
return named_tuple{std::move(tuple), id_ts{}};
}
}

template<typename... Named_Tuple_Ts>
[[nodiscard]] constexpr auto concat_named_tuple(Named_Tuple_Ts &&...named_tuples) {
using id_ts = detail::variadic::concat_t<typename Named_Tuple_Ts::id_ts...>;

auto content = std::tuple_cat(UPD_FWD(named_tuples).m_content...);
auto name_content = [&](auto... id_iconsts) { return name_tuple<id_iconsts.value...>(std::move(content)); };

return std::apply(name_content, id_ts{});
}

} // namespace upd
#include "named_value.hpp"
Loading

0 comments on commit a9698f7

Please sign in to comment.