Skip to content

Latest commit

 

History

History
172 lines (140 loc) · 5.3 KB

235.md

File metadata and controls

172 lines (140 loc) · 5.3 KB
Info

Example

template<class T, fixed_string Str, auto Offset = 0>
struct field {
  constexpr const auto& get() const { return *reinterpret_cast<const T*>(reinterpret_cast<const char*>(this) + Offset); }
  constexpr operator const T&() const { return get(); }
};

enum class side : std::uint8_t { buy, sell };
struct trade {
  [[no_unique_address]] field<::std::int32_t, "price"> price;
  [[no_unique_address]] field<::std::uint32_t, "size", sizeof(std::int32_t)> quantity;
  [[no_unique_address]] field<::side, "side", sizeof(std::int32_t) + sizeof(std::uint32_t)> side;
};
static_assert(1 == sizeof(trade));

auto parse(const void* msg) {
  const auto& trade = *reinterpret_cast<const ::trade*>(msg);
  assert(42 == trade.price);
  assert(100 == trade.quantity);
  assert(side::sell == trade.side);
}

int main() {
  struct [[gnu::packed]] {
    ::std::int32_t price{42};
    ::std::uint32_t quantity{100};
    ::side side{side::sell};
  } trade{};

  parse(std::addressof(trade));
}

https://godbolt.org/z/3r8hq5sxe

Puzzle

  • Can you implement buffer_cast and extend field to support dynamic arrays?
template<class T, fixed_string Name, auto Offset = 0>
struct field; // TODO
template<class T> const T& buffer_cast(const void* msg); // TODO

enum class side : std::uint8_t { buy, sell };
struct trade final {
  [[no_unique_address]] field<::std::int32_t, "price"> price;
  [[no_unique_address]] field<::std::uint32_t, "size", sizeof(std::int32_t)> quantity;
  [[no_unique_address]] field<::side, "side", sizeof(std::int32_t) + sizeof(std::uint32_t)> side;
};
static_assert(1 == sizeof(trade));

struct order final {
  [[no_unique_address]] field<::std::int32_t, "price"> price;
  [[no_unique_address]] field<::std::uint32_t, "size", sizeof(std::int32_t)> quantity;
  [[no_unique_address]] field<::side, "side", sizeof(std::int32_t) + sizeof(std::uint32_t)> side;
  [[no_unique_address]] field<::std::uint32_t, "len", sizeof(std::int32_t) + sizeof(std::uint32_t) + sizeof(::side)> len;
  [[no_unique_address]] field<double[], "prices", sizeof(std::int32_t) + sizeof(std::uint32_t) + sizeof(::side) + sizeof(::std::uint32_t)> prices;
};
static_assert(1 == sizeof(order));

int main() {
  using namespace boost::ut;

  "buffer cast"_test = [] {
    struct [[gnu::packed]] {
      ::std::int32_t price{1};
      ::std::uint32_t quantity{2};
      ::side side{side::buy};
    } trade{};

    const auto& msg = buffer_cast<::trade>(std::addressof(trade));

    expect(1 == msg.price);
    expect(2 == msg.quantity);
    expect(side::buy == msg.side);
  };

  "buffer cast with dynamic array at the end"_test = [] {
    struct [[gnu::packed]] {
      ::std::int32_t price{42};
      ::std::uint32_t quantity{100};
      ::side side{side::sell};
      ::std::uint32_t len{4};
      std::array<double, 4> prices{.4, .3, .2, .1};
    } order{};

    const auto& msg = buffer_cast<::order>(std::addressof(order));

    expect(42 == msg.price);
    expect(100 == msg.quantity);
    expect(side::sell == msg.side);
    expect(4 == msg.len);
    expect(.4 == msg.prices[0]);
    expect(.3 == msg.prices[1]);
    expect(.2 == msg.prices[2]);
    expect(.1 == msg.prices[3]);
  };
}

https://godbolt.org/z/ca8d565TE

Solutions

template<class T, fixed_string Name, auto Offset = 0>
struct field {
  [[nodiscard]] constexpr operator const T&() const noexcept {
    return *std::launder(reinterpret_cast<const T*>(reinterpret_cast<const char*>(this) + Offset));
  }
};
template<class T> [[nodiscard]] constexpr const T& buffer_cast(const void* msg) noexcept {
  return *(::new (const_cast<void*>(msg)) const T);
}

https://godbolt.org/z/1T96och5x

template<class T, fixed_string Name, auto Offset = 0>
struct field {
  constexpr const auto& get() const { return *reinterpret_cast<const T*>(reinterpret_cast<const char*>(this) + Offset); }
  constexpr operator const T&() const { return get(); }
};
template<class T> const T& buffer_cast(const void* msg) {
  return *reinterpret_cast<const T*>(msg);
}

https://godbolt.org/z/K9E8Kd73x

template<class T, fixed_string Name, auto Offset = 0>
struct field {
    [[nodiscard]] constexpr const T& get() const noexcept {
        // I'm pretty sure this is UB... since the struct is packed so the
        // values are not aligned... :(
        const auto addr = reinterpret_cast<const std::byte*>(this) + Offset;
        return *std::launder(reinterpret_cast<const T*>(addr));
    }

    constexpr operator const T&() const noexcept { return get(); }
};

https://godbolt.org/z/49vnv9Pz8

template <class T, fixed_string Name, auto Offset = 0>
struct field {
  [[nodiscard]] constexpr const auto& get() const {
    return *reinterpret_cast<const T*>(reinterpret_cast<const char*>(this) +
                                       Offset);
  }
  [[nodiscard]] constexpr operator const T&() const { return get(); }
};
template <class T>
[[nodiscard]] const T& buffer_cast(const void* msg) {
  return *reinterpret_cast<const T*>(msg);
}

https://godbolt.org/z/1T61fKoM8