Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions include/msg/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,10 @@ template <stdx::ct_string Name, typename... Fields> class msg_access {
using FieldsTuple =
decltype(stdx::make_indexed_tuple<name_for>(Fields{}...));

template <typename Field, stdx::range R> constexpr static auto check() {
template <typename N, stdx::range R> constexpr static auto check() {
static_assert((std::is_same_v<N, name_for<Fields>> or ...),
"Field does not belong to this message!");
using Field = field_t<N>;
constexpr auto belongs = (std::is_same_v<typename Field::field_id,
typename Fields::field_id> or
...);
Expand All @@ -209,20 +212,20 @@ template <stdx::ct_string Name, typename... Fields> class msg_access {

template <stdx::range R, some_field_value V>
constexpr static auto set1(R &&r, V v) -> void {
check<name_for<V>, std::remove_cvref_t<R>>();
using Field = field_t<name_for<V>>;
check<Field, std::remove_cvref_t<R>>();
Field::insert(std::forward<R>(r),
static_cast<typename Field::value_type>(v.value));
}

template <typename N, stdx::range R>
constexpr static auto set_default(R &&r) -> void {
check<field_t<N>, std::remove_cvref_t<R>>();
check<N, std::remove_cvref_t<R>>();
field_t<N>::insert_default(std::forward<R>(r));
}

template <typename N, stdx::range R> constexpr static auto get(R &&r) {
check<field_t<N>, std::remove_cvref_t<R>>();
check<N, std::remove_cvref_t<R>>();
return field_t<N>::extract(std::forward<R>(r));
}

Expand Down Expand Up @@ -341,11 +344,36 @@ template <stdx::ct_string Name, typename Access, typename T> struct msg_base {
[[nodiscard]] constexpr auto get(auto f) const {
return Access::get(as_derived().data(), f);
}

constexpr auto set(auto... fs) -> void {
Access::set(as_derived().data(), fs...);
}
constexpr auto set() -> void {}

template <stdx::ct_string N> struct proxy {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-const-or-ref-data-members)
msg_base &b;

// NOLINTNEXTLINE(misc-unconventional-assign-operator)
constexpr auto operator=(auto val) const && -> void {
b.set(field_name<N>{} = val);
}

using V = decltype(b.get(std::declval<field_name<N>>()));

// NOLINTNEXTLINE(google-explicit-constructor)
constexpr operator V() const { return b.get(field_name<N>{}); }
};

template <stdx::ct_string N>
[[nodiscard]] constexpr auto operator[](field_name<N> f) const {
return get(f);
}
template <stdx::ct_string N>
[[nodiscard]] constexpr auto operator[](field_name<N>) LIFETIMEBOUND {
return proxy<N>{*this};
}

[[nodiscard]] constexpr auto describe() const {
return Access::describe(as_derived().data());
}
Expand Down
4 changes: 4 additions & 0 deletions test/msg/fail/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ add_compile_fail_test(message_cmp_view.cpp LIBRARIES warnings cib_msg)
add_compile_fail_test(message_const_field_write.cpp LIBRARIES warnings cib_msg)
add_compile_fail_test(message_dangling_view.cpp LIBRARIES warnings cib_msg)
add_compile_fail_test(message_dup_fieldnames.cpp LIBRARIES warnings cib_msg)
add_compile_fail_test(message_get_nonexistent_field.cpp LIBRARIES warnings
cib_msg)
add_compile_fail_test(message_incompatible_matcher.cpp LIBRARIES warnings
cib_msg)
add_compile_fail_test(message_set_nonexistent_field.cpp LIBRARIES warnings
cib_msg)
add_compile_fail_test(message_uninitialized_field.cpp LIBRARIES warnings
cib_msg)
add_compile_fail_test(view_upsize.cpp LIBRARIES warnings cib_msg)
17 changes: 17 additions & 0 deletions test/msg/fail/message_get_nonexistent_field.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <msg/field.hpp>
#include <msg/message.hpp>

// EXPECT: Field does not belong to this message
namespace {
using namespace msg;

using test_field1 =
field<"test_field", std::uint32_t>::located<at{0_dw, 31_msb, 24_lsb}>;

using msg_defn = message<"test_msg", test_field1>;
} // namespace

auto main() -> int {
owning<msg_defn> m{};
[[maybe_unused]] auto f = m.get("no"_field);
}
17 changes: 17 additions & 0 deletions test/msg/fail/message_set_nonexistent_field.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <msg/field.hpp>
#include <msg/message.hpp>

// EXPECT: Field does not belong to this message
namespace {
using namespace msg;

using test_field1 =
field<"test_field", std::uint32_t>::located<at{0_dw, 31_msb, 24_lsb}>;

using msg_defn = message<"test_msg", test_field1>;
} // namespace

auto main() -> int {
owning<msg_defn> m{};
m.set("no"_field = 1);
}
15 changes: 15 additions & 0 deletions test/msg/message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -792,3 +792,18 @@ TEST_CASE("pack appends environments", "[message]") {
using defn = pack<"defn", std::uint8_t, m1, m2>;
STATIC_REQUIRE(custom(defn::env_t{}) == 18);
}

TEST_CASE("read indexing operator on message", "[message]") {
test_msg const msg{};
CHECK(0x80 == msg["id"_field]);
}

// This test causes clang-14 with libc++ to crash...
#if not __clang__ or __clang_major__ > 14
TEST_CASE("write indexing operator on message", "[message]") {
test_msg msg{};
CHECK((0 == msg["f1"_field]));
msg["f1"_field] = 0xba11;
CHECK((0xba11 == msg["f1"_field]));
}
#endif
Loading