Skip to content

Latest commit

 

History

History
550 lines (468 loc) · 17.5 KB

308.md

File metadata and controls

550 lines (468 loc) · 17.5 KB
Info

Example

struct unpacked {
  char a;  // size: 1b => size: 4b
  int  b;  // size: 4b => size: 4b
  char c;  // size: 1b => size: 4b
           //             ---------
           //             size: 12b
};

struct packed {
  char a;  // size: 1b => size: 4b
  char b;  // size: 1b => size: 4b
  int  c;  // size: 4b => size: 8b
           //             --------
           //             size: 8b
};

static_assert(12 == sizeof(unpacked));
static_assert(8 == sizeof(packed));

https://godbolt.org/z/q794afz8E

Puzzle

  • Can you implement is_packed_layout trait which for given type T will return { true: if the sizeof...(T.fields) == sizeof(T) or alignments of T.fields are sorted, false : otherwise }?
template<class T> constexpr auto is_packed_layout_v;

struct unpacked {
  char a;  // size: 1b => size: 4b
  int  b;  // size: 4b => size: 4b
  char c;  // size: 1b => size: 4b
           //             ---------
           //             size: 12b
};

struct unpacked1 {
  int  b;  // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  int c;   // size: 4b => size: 12b
           //             ---------
           //             size: 12b
};

struct packed {
  char a;  // size: 1b => size: 4b
  char b;  // size: 1b => size: 4b
  int  c;  // size: 4b => size: 8b
           //             --------
           //             size: 8b
};

struct packed1 {
  int a;   // size: 4b => size: 4b
           //             --------
           //             size: 4b
};

struct packed2 {
  int  c;  // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  char b;  // size: 1b => size: 8b
           //             --------
           //             size: 8b
};

struct packed3 {
  int x;   // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  char b;  // size: 1b => size: 8b
  char c;  // size: 1b => size: 8b
  char d;  // size: 1b => size: 8b
  int y;   // size: 4b => size: 12b
           //             ---------
           //             size: 12b
};

struct packed4 {
  using a3_t = std::array<char, 3>;
  short x; // size: 2b => size: 2b
  short y; // size: 2b => size: 4b
  a3_t z;  // size: 3b => size: 8b
           //             --------
           //             size: 8b
};

struct packed5 {
  short x; // size: 2b => size: 2b
  short y; // size: 2b => size: 4b
  short z; // size: 2b => size: 6b
           //             --------
           //             size: 6b
};

struct empty {
           // size: 1b => size: 1b
           //             --------
           //             size: 1b
};

static_assert(12 == sizeof(unpacked));
static_assert(not is_packed_layout_v<unpacked>);

static_assert(12 == sizeof(unpacked1));
static_assert(not is_packed_layout_v<unpacked1>);

static_assert(8 == sizeof(packed));
static_assert(is_packed_layout_v<packed>);

static_assert(4 == sizeof(packed1));
static_assert(is_packed_layout_v<packed1>);

static_assert(8 == sizeof(packed2));
static_assert(is_packed_layout_v<packed2>);

static_assert(12 == sizeof(packed3));
static_assert(is_packed_layout_v<packed3>);

static_assert(8 == sizeof(packed4));
static_assert(is_packed_layout_v<packed4>);

static_assert(6 == sizeof(packed5));
static_assert(is_packed_layout_v<packed5>);

static_assert(1 == sizeof(empty));
static_assert(is_packed_layout_v<empty>);

https://godbolt.org/z/x17PMbEWe

Solutions

#include <algorithm>
#include <array>
#include <type_traits>

template <class T, class... TArgs> decltype(void(T{std::declval<TArgs>()...}), std::true_type{}) test_is_braces_constructible(int);
template <class, class...> std::false_type test_is_braces_constructible(...);
template <class T, class... TArgs> using is_braces_constructible = decltype(test_is_braces_constructible<T, TArgs...>(0));

struct any_type {
  template<class T> constexpr operator T();
};

template<class T> constexpr bool size_of_fields_equals_size_of_struct() noexcept {
  constexpr T object{};
  if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3, m4, m5] = object;
      return sizeof(T) == sizeof(m0) + sizeof(m1) + sizeof(m2) + sizeof(m3) + sizeof(m4) + sizeof(m5);
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3, m4] = object;
      return sizeof(T) == sizeof(m0) + sizeof(m1) + sizeof(m2) + sizeof(m3) + sizeof(m4);
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3] = object;
      return sizeof(T) == sizeof(m0) + sizeof(m1) + sizeof(m2) + sizeof(m3);
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2] = object;
      return sizeof(T) == sizeof(m0) + sizeof(m1) + sizeof(m2);
  } else if constexpr(is_braces_constructible<T, any_type, any_type>{}) {
      auto&& [m0, m1] = object;
      return sizeof(T) == sizeof(m0) + sizeof(m1);
  } else if constexpr(is_braces_constructible<T, any_type>{}) {
      auto&& [m0] = object;
      return sizeof(T) == sizeof(m0);
  } else {
      return true;
  }
}

template<class T> constexpr bool alignments_sorted() noexcept {
  constexpr T object{};
  if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3, m4, m5] = object;
      std::array<int, 6> sizes{sizeof(m0), sizeof(m1), sizeof(m2), sizeof(m3), sizeof(m4), sizeof(m5)};
      return std::is_sorted(sizes.cbegin(), sizes.cend(), std::less{}) || std::is_sorted(sizes.cbegin(), sizes.cend(), std::greater{});
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3, m4] = object;
      std::array<int, 5> sizes{sizeof(m0), sizeof(m1), sizeof(m2), sizeof(m3), sizeof(m4)};
      return std::is_sorted(sizes.cbegin(), sizes.cend(), std::less{}) || std::is_sorted(sizes.cbegin(), sizes.cend(), std::greater{});
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2, m3] = object;
      std::array<int, 4> sizes{sizeof(m0), sizeof(m1), sizeof(m2), sizeof(m3)};
      return std::is_sorted(sizes.cbegin(), sizes.cend(), std::less{}) || std::is_sorted(sizes.cbegin(), sizes.cend(), std::greater{});
  } else if constexpr(is_braces_constructible<T, any_type, any_type, any_type>{}) {
      auto&& [m0, m1, m2] = object;
      std::array<int, 3> sizes{sizeof(m0), sizeof(m1), sizeof(m2)};
      return std::is_sorted(sizes.cbegin(), sizes.cend(), std::less{}) || std::is_sorted(sizes.cbegin(), sizes.cend(), std::greater{});
  } else if constexpr(is_braces_constructible<T, any_type, any_type>{}) {
      return true;
  } else if constexpr(is_braces_constructible<T, any_type>{}) {
      return true;
  } else {
      return true;
  }
}

template<class T> constexpr auto is_packed_layout_v = size_of_fields_equals_size_of_struct<T>() || alignments_sorted<T>();

struct unpacked {
  char a;  // size: 1b => size: 4b
  int  b;  // size: 4b => size: 4b
  char c;  // size: 1b => size: 4b
           //             ---------
           //             size: 12b
};

struct unpacked1 {
  int  b;  // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  int c;   // size: 4b => size: 12b
           //             ---------
           //             size: 12b
};

struct packed {
  char a;  // size: 1b => size: 4b
  char b;  // size: 1b => size: 4b
  int  c;  // size: 4b => size: 8b
           //             --------
           //             size: 8b
};

struct packed1 {
  int a;   // size: 4b => size: 4b
           //             --------
           //             size: 4b
};

struct packed2 {
  int  c;  // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  char b;  // size: 1b => size: 8b
           //             --------
           //             size: 8b
};

struct packed3 {
  int x;   // size: 4b => size: 4b
  char a;  // size: 1b => size: 8b
  char b;  // size: 1b => size: 8b
  char c;  // size: 1b => size: 8b
  char d;  // size: 1b => size: 8b
  int y;   // size: 4b => size: 12b
           //             ---------
           //             size: 12b
};

struct packed4 {
  using a3_t = std::array<char, 3>;
  short x; // size: 2b => size: 2b
  short y; // size: 2b => size: 4b
  a3_t z;  // size: 3b => size: 8b
           //             --------
           //             size: 8b
};

struct packed5 {
  short x; // size: 2b => size: 2b
  short y; // size: 2b => size: 4b
  short z; // size: 2b => size: 6b
           //             --------
           //             size: 6b
};

struct empty {
           // size: 1b => size: 1b
           //             --------
           //             size: 1b
};

static_assert(12 == sizeof(unpacked));
static_assert(not is_packed_layout_v<unpacked>);

static_assert(12 == sizeof(unpacked1));
static_assert(not is_packed_layout_v<unpacked1>);

static_assert(8 == sizeof(packed));
static_assert(is_packed_layout_v<packed>);

static_assert(4 == sizeof(packed1));
static_assert(is_packed_layout_v<packed1>);

static_assert(8 == sizeof(packed2));
static_assert(is_packed_layout_v<packed2>);

static_assert(12 == sizeof(packed3));
static_assert(is_packed_layout_v<packed3>);

static_assert(8 == sizeof(packed4));
static_assert(is_packed_layout_v<packed4>);

static_assert(6 == sizeof(packed5));
static_assert(is_packed_layout_v<packed5>);

static_assert(1 == sizeof(empty));
static_assert(is_packed_layout_v<empty>);

https://godbolt.org/z/46orYesoW

template<auto Id>
struct alignment {
  std::size_t *alignments, *sizeofs{};
  template<class T> constexpr operator T() const {
    alignments[Id] = alignof(T);
    sizeofs[Id] = sizeof(T);
    return {};
  }
};

template<class T, auto... Ns>
constexpr auto is_packed_layout(std::index_sequence<Ns...>) {
  if constexpr(sizeof...(Ns) <= 1) {
    return true;
  } else if constexpr(requires { T{alignment<Ns>{}...}; }) {
    std::size_t alignments[sizeof...(Ns)]{}, sizeofs[sizeof...(Ns)]{};
    void(T{alignment<Ns>{alignments, sizeofs}...});
    return sizeof(T) == (sizeofs[Ns] + ...) or
           std::is_sorted(std::cbegin(alignments), std::cend(alignments), [](const auto lhs, const auto rhs) { return lhs > rhs; }) or
           std::is_sorted(std::cbegin(alignments), std::cend(alignments), [](const auto lhs, const auto rhs) { return lhs < rhs; });
  } else {
    return is_packed_layout<T>(std::make_index_sequence<sizeof...(Ns) - 1>{});
  }
}

template<class T> requires std::is_aggregate_v<T>
constexpr std::bool_constant<is_packed_layout<T>(
  std::make_index_sequence<sizeof(T)>{})> is_packed_layout_v{};

https://godbolt.org/z/69bqr6r6W

template <class T>
[[nodiscard]] constexpr auto to_tuple(T&& obj) {
    // clang-format off
  if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5, p6, p7, p8, p9, p10] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5, p6, p7, p8, p8, p10);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5, p6, p7, p8, p9] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5, p6, p7, p8, p9] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5, p6, p7, p8, p9);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5, p6, p7, p8] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5, p6, p7, p8] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5, p6, p7, p8);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5, p6, p7] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5, p6, p7] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5, p6, p7);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5, p6] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5, p6] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5, p6);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4, p5] = obj; }; }) {
    auto&& [p1, p2, p3, p4, p5] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4, p5);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3, p4] = obj; }; }) {
    auto&& [p1, p2, p3, p4] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3, p4);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2, p3] = obj; }; }) {
    auto&& [p1, p2, p3] = std::forward<T>(obj);
    return std::make_tuple(p1, p2, p3);
  } else if constexpr (requires { [&obj] { auto&& [p1, p2] = obj; }; }) {
    auto&& [p1, p2] = std::forward<T>(obj);
    return std::make_tuple(p1, p2);
  } else if constexpr (requires { [&obj] { auto&& [p1] = obj; }; }) {
    auto&& [p1] = std::forward<T>(obj);
    return std::make_tuple(p1);
  } else {
    return std::make_tuple();
  }
    // clang-format on
}

template <class T>
consteval bool is_packed_layout() {
    auto t = to_tuple(T{});
    if constexpr (std::tuple_size_v<decltype(t)> == 0) {
        return true;
    } else {
        std::size_t sum = 0;
        std::size_t current_size = sizeof(std::get<0>(t));
        bool ascending = true;
        bool descending = true;
        std::apply(
            [&](auto&&... t) {
                ((sum += sizeof(t),
                ascending = ascending and sizeof(t) >= current_size,
                descending = descending and sizeof(t) <= current_size,
                current_size = sizeof(t)), ...);
            },
            t);
        return (sum == sizeof(T)) or ascending or descending;
    }
}

https://godbolt.org/z/v1vbToYbx

template<auto Ns>
struct AnyType {
    std::size_t &size, &align;
    template<class T> constexpr operator T() const {
        size = sizeof(T);
        align = alignof(T);
        return T{};
    }
};

template<class T, auto...Ns>
constexpr auto is_packed_layout(std::index_sequence<Ns...>) {
    if constexpr(sizeof...(Ns) <=1) {
        return true;
    } else if constexpr(std::is_constructible_v<T, AnyType<Ns>...>) {
        std::array<std::size_t, sizeof...(Ns)> sizeofs, alignofs;
        void(T{AnyType<Ns>{sizeofs[Ns], alignofs[Ns]}...});
        return sizeof(T) == (sizeofs[Ns] + ...) or
            std::is_sorted(std::crbegin(alignofs), std::crend(alignofs)) or
            std::is_sorted(std::cbegin(alignofs), std::cend(alignofs));
    } else {
        return is_packed_layout<T>(std::make_index_sequence<sizeof...(Ns)-1>{});
    }
}

template<class T> constexpr auto is_packed_layout_v =
is_packed_layout<T>(std::make_index_sequence<sizeof(T)>{});

https://godbolt.org/z/rbxo1nd1P

namespace detail {

template <class T>
auto is_packed(T v) {
  const auto &[... vs] = v;
  constexpr auto packed = (sizeof(T) - ... - sizeof(vs)) < alignof(T);
  return std::bool_constant<packed>{};
}

}  // namespace detail

template <class T>
constexpr auto is_packed_layout_v =
    std::is_empty_v<T> or decltype(detail::is_packed(std::declval<T>()))::value;

https://godbolt.org/z/e9WKjdE9G

namespace detail {
auto is_packed(auto v) {
  auto&& [... vs] = v;
  constexpr auto sizes = std::array<std::size_t, sizeof...(vs)>{sizeof(vs)...};
  constexpr auto packed =
      std::reduce(sizes.begin(), sizes.end()) == sizeof(v) or
      std::is_sorted(sizes.begin(), sizes.end(), std::less{}) or
      std::is_sorted(sizes.begin(), sizes.end(), std::greater{});
  return std::bool_constant<packed>{};
}
}  // namespace detail

template <class T>
inline constexpr bool is_packed_layout_v =
    decltype(detail::is_packed(std::declval<T>()))::value

https://godbolt.org/z/WPTbbfaqW

namespace mp = boost::mp;
template <auto Fn>
constexpr auto sort = [](std::ranges::range auto types) {
  std::sort(std::begin(types), std::end(types), Fn);
  return types;
};
using mp::operator|;
auto by_size = [](auto lhs, auto rhs) { return lhs.size < rhs.size; };
constexpr auto pack = [](auto t) {
  return mp::reflection::to_tuple(t) | sort<by_size>;
};

template <class T> constexpr auto is_packed_layout_v =
    sizeof(pack(std::declval<T>())) == sizeof(T);

https://godbolt.org/z/6MsvK6onv

template<int N>
struct Foo{
    template<class T> requires (sizeof(T) >=N)
    constexpr operator T() const {
        return {};
    }
    auto constexpr next()
    {
        return Foo<N+1>{};
    }
    auto constexpr size()
    {
        return N;
    }
};
template<typename T>
constexpr bool reverse_constructable( auto const & ... args )
{
    auto constexpr N = sizeof ...(args);
    return [&args...]<auto ... Is>(std::index_sequence<Is...>)
    {
        auto t = std::tie(args...);
        return requires{ T{std::get<N-1-Is>(t)...};};
    }( std::make_index_sequence<sizeof...(args)>());
}

template<class T> constexpr auto is_packed_layout(auto first, auto ... args)
{
    if constexpr ( reverse_constructable<T>(first.next(),args...) )
    {
        return is_packed_layout<T>( first.next(), args...);
    } else if constexpr ( reverse_constructable<T>(Foo<1>{}, first, args...) )
        return is_packed_layout<T>(Foo<1>{}, first, args...);
    else
    {
        auto sum = (first.size()+ ... + args.size() );
        int c0, c1 = first.size();
        auto is_sorted1 = ((c0 = c1, c0 >= (c1 = args.size())) && ...);
        c1 = first.size();
        auto is_sorted2 = ((c0 = c1, c0 <= (c1 = args.size())) && ...);
        return sum == sizeof(T) || is_sorted1 ||is_sorted2;
    }
}

template<class T> constexpr auto is_packed_layout_v= sizeof(T) <=1 || is_packed_layout<T>(Foo<1>{});

https://godbolt.org/z/6fWW3easG