Skip to content
Open
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
26 changes: 26 additions & 0 deletions docs/bit.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,32 @@ constexpr std::size_t x = stdx::bit_size<std::uint8_t>();
static_assert(x == 8);
----

=== `bit_structure`

`bit_structure` is the opposite of
xref:bit.adoc#_bit_destructure[`bit_destructure`]: a function for packing
several unsigned integral values into a larger bit width value. It is a more
general case of xref:bit.adoc#_bit_pack[`bit_pack`].

[source,cpp]
----
std::uint32_t const x = 0x1234'5678u;
auto const [a, b, c] = stdx::bit_destructure<8, 24>(x);
// a = 0x78u, b = 0x3456u, c = 0x12u
// a, b and c are all std::uint32_t

auto const x = stdx::bit_structure<8, 24>(a, b, c);
// x = 0x1234'5678u (a std::uint32_t)

// or:
auto const y = stdx::bit_structure<std::uint64_t, 8, 24>(a, b, c);
// y = 0x1234'5678u (a std::uint64_t)
----

NOTE: Unlike `bit_pack`, `bit_structure` takes the arguments in
_increasing_ order of significance, and the template arguments representing the
split boundaries must be in increasing order.

=== `bit_unpack`

`bit_unpack` is a function for unpacking an unsigned integral values into multiple
Expand Down
18 changes: 18 additions & 0 deletions include/stdx/bit.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -462,5 +462,23 @@ constexpr auto bit_destructure(T t) -> std::array<T, sizeof...(Offsets) + 1> {
return bit_detail::bit_destructure_impl<Offsets..., bit_size<T>()>(
t, std::make_index_sequence<sizeof...(Offsets) + 1>{});
}

template <unsigned_integral T, std::size_t... Offsets, unsigned_integral... Ts>
requires(sizeof...(Ts) - sizeof...(Offsets) == 1)
constexpr auto bit_structure(Ts... ts) -> T {
constexpr auto shifted = []<std::size_t Offset>(auto x) -> T {
return static_cast<T>(static_cast<T>(x) << Offset);
};
return [&]<std::size_t... Os>() -> T {
return (T{} | ... | shifted.template operator()<Os>(ts));
}.template operator()<std::size_t{}, Offsets...>();
}

template <std::size_t... Offsets, unsigned_integral... Ts>
requires(sizeof...(Ts) - sizeof...(Offsets) == 1)
constexpr auto bit_structure(Ts... ts) -> std::common_type_t<Ts...> {
using T = std::common_type_t<Ts...>;
return bit_structure<T, Offsets...>(ts...);
}
} // namespace v1
} // namespace stdx
36 changes: 36 additions & 0 deletions test/bit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,3 +459,39 @@ TEST_CASE("bit_destructure (split in three)", "[bit]") {
CHECK(b == 0x3456u);
CHECK(c == 0x12u);
}

TEST_CASE("bit_structure (degenerate case)", "[bit]") {
constexpr auto x = stdx::bit_structure<std::uint16_t>(0b11u);
STATIC_CHECK(x == 0b11u);
STATIC_CHECK(std::same_as<decltype(x), std::uint16_t const>);
}

TEST_CASE("bit_structure (two parts)", "[bit]") {
constexpr auto x = stdx::bit_structure<std::uint16_t, 2>(0b01u, 0b10u);
STATIC_CHECK(x == 0b10'01u);
STATIC_CHECK(std::same_as<decltype(x), std::uint16_t const>);
}

TEST_CASE("bit_structure (three parts)", "[bit]") {
constexpr auto x =
stdx::bit_structure<std::uint32_t, 3, 5>(0b01u, 0b10u, 0b10u);
STATIC_CHECK(x == 0b10'10'001u);
STATIC_CHECK(std::same_as<decltype(x), std::uint32_t const>);
}

TEST_CASE("bit_{de}structure round-trip", "[bit]") {
constexpr auto x = std::uint32_t{0x1234'5678u};
auto [a, b, c] = stdx::bit_destructure<8, 24>(x);
auto y = stdx::bit_structure<std::uint32_t, 8, 24>(a, b, c);
CHECK(x == y);
}

TEST_CASE("bit_structure (inferred type)", "[bit]") {
constexpr auto x = std::uint32_t{0x1234'5678u};
auto [a, b, c] = stdx::bit_destructure<8, 24>(x);
STATIC_CHECK(std::same_as<decltype(a), std::uint32_t>);
STATIC_CHECK(std::same_as<decltype(b), std::uint32_t>);
STATIC_CHECK(std::same_as<decltype(c), std::uint32_t>);
auto y = stdx::bit_structure<8, 24>(a, b, c);
CHECK(x == y);
}