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
160 changes: 160 additions & 0 deletions include/stdx/bitset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <stdx/type_traits.hpp>
#include <stdx/udls.hpp>

#include <boost/mp11/algorithm.hpp>

#include <algorithm>
#include <array>
#include <cstddef>
Expand Down Expand Up @@ -478,5 +480,163 @@ template <typename T, typename F, typename R, auto M, typename... S>
#if __cplusplus >= 202002L
template <std::size_t N> bitset(ct_string<N>) -> bitset<N - 1>;
#endif

namespace detail {
template <typename...> constexpr std::size_t index_of = 0;

template <typename T, typename... Us>
constexpr std::size_t index_of<T, type_list<Us...>> =
boost::mp11::mp_find<type_list<Us...>, T>::value;
} // namespace detail

template <typename... Ts> class type_bitset {
using list_t = boost::mp11::mp_unique<type_list<Ts...>>;
constexpr static std::size_t N = boost::mp11::mp_size<list_t>::value;

bitset<N> bs;

[[nodiscard]] friend constexpr auto operator==(type_bitset const &lhs,
type_bitset const &rhs)
-> bool {
return lhs.bs == rhs.bs;
}

friend constexpr auto operator|(type_bitset lhs, type_bitset const &rhs)
-> type_bitset {
lhs |= rhs;
return lhs;
}

friend constexpr auto operator&(type_bitset lhs, type_bitset const &rhs)
-> type_bitset {
lhs &= rhs;
return lhs;
}

friend constexpr auto operator^(type_bitset lhs, type_bitset const &rhs)
-> type_bitset {
lhs ^= rhs;
return lhs;
}

friend constexpr auto operator-(type_bitset const &lhs, type_bitset rhs)
-> type_bitset {
rhs.flip();
return lhs & rhs;
}

template <typename F, std::size_t... Is>
constexpr static auto make_callers(std::index_sequence<Is...>) {
using call_t = auto (*)(F &)->void;
return std::array<call_t, N>{[](F &f) {
f.template operator()<boost::mp11::mp_at_c<list_t, Is>, Is>();
}...};
}

public:
constexpr type_bitset() = default;
constexpr explicit type_bitset(all_bits_t) : bs{all_bits} {}
constexpr explicit type_bitset(std::uint64_t value) : bs{value} {}

template <typename... Us>
constexpr explicit type_bitset(type_list<Us...>)
: bs{place_bits, detail::index_of<Us, list_t>...} {
static_assert((... and (detail::index_of<Us, list_t> < N)),
"Type not found in bitset");
}

template <typename T> [[nodiscard]] constexpr auto to() const -> T {
return bs.template to<T>();
}
[[nodiscard]] constexpr auto to_natural() const { return bs.to_natural(); }

constexpr static std::integral_constant<std::size_t, N> size{};

template <typename T>
[[nodiscard]] constexpr auto operator[](type_identity<T>) const
-> decltype(auto) {
constexpr auto idx = detail::index_of<T, list_t>;
static_assert(idx < sizeof...(Ts), "Type not found in bitset");
return bs[idx];
}

template <typename... Us>
constexpr auto set(bool value = true) LIFETIMEBOUND -> type_bitset & {
static_assert((... and (detail::index_of<Us, type_list<Ts...>> < N)),
"Type not found in bitset");
if constexpr (sizeof...(Us) == 0) {
bs.set();
} else {
(bs.set(detail::index_of<Us, type_list<Ts...>>, value), ...);
}
return *this;
}

template <typename... Us>
constexpr auto reset() LIFETIMEBOUND -> type_bitset & {
static_assert((... and (detail::index_of<Us, type_list<Ts...>> < N)),
"Type not found in bitset");
if constexpr (sizeof...(Us) == 0) {
bs.reset();
} else {
(bs.reset(detail::index_of<Us, type_list<Ts...>>), ...);
}
return *this;
}

template <typename... Us>
constexpr auto flip() LIFETIMEBOUND -> type_bitset & {
static_assert((... and (detail::index_of<Us, type_list<Ts...>> < N)),
"Type not found in bitset");
if constexpr (sizeof...(Us) == 0) {
bs.flip();
} else {
(bs.flip(detail::index_of<Us, type_list<Ts...>>), ...);
}
return *this;
}

[[nodiscard]] constexpr auto count() const -> std::size_t {
return bs.count();
}

[[nodiscard]] constexpr auto all() const -> bool {
return count() == size();
}

[[nodiscard]] constexpr auto any() const -> bool {
return count() != std::size_t{};
}

[[nodiscard]] constexpr auto none() const -> bool { return not any(); }

[[nodiscard]] constexpr auto operator~() const -> type_bitset {
return type_bitset{~bs.template to<std::uint64_t>()};
}

constexpr auto
operator|=(type_bitset const &rhs) LIFETIMEBOUND->type_bitset & {
bs |= rhs.bs;
return *this;
}

constexpr auto
operator&=(type_bitset const &rhs) LIFETIMEBOUND->type_bitset & {
bs &= rhs.bs;
return *this;
}

constexpr auto
operator^=(type_bitset const &rhs) LIFETIMEBOUND->type_bitset & {
bs ^= rhs.bs;
return *this;
}

template <typename F> constexpr auto for_each(F &&f) const -> F {
constexpr auto callers = make_callers<F>(std::make_index_sequence<N>{});
stdx::for_each([&](auto i) { callers[i](f); }, bs);
return f;
}
};
} // namespace v1
} // namespace stdx
2 changes: 2 additions & 0 deletions include/stdx/type_traits.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ template <typename T> struct type_identity {
using type = T;
};
template <typename T> using type_identity_t = typename type_identity<T>::type;
template <typename T>
constexpr static auto type_identity_v = type_identity<T>{};

namespace detail {
template <typename T, template <typename...> typename U>
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ add_tests(
rollover
span
to_underlying
type_bitset
type_map
type_traits
with_result_of
Expand Down
172 changes: 172 additions & 0 deletions test/type_bitset.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#include <stdx/bitset.hpp>
#include <stdx/ct_conversions.hpp>

#include <catch2/catch_template_test_macros.hpp>
#include <catch2/catch_test_macros.hpp>

#include <cstddef>
#include <cstdint>
#include <string>
#include <type_traits>

TEST_CASE("bitset size", "[type_bitset]") {
STATIC_CHECK(stdx::type_bitset<int>{}.size() == 1u);
STATIC_CHECK(stdx::type_bitset<int, float>{}.size() == 2u);
}

TEST_CASE("index operation", "[type_bitset]") {
STATIC_CHECK(not stdx::type_bitset<int>{}[stdx::type_identity_v<int>]);
}

TEST_CASE("set single bit", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{};
CHECK(not bs[stdx::type_identity_v<int>]);
bs.set<int>();
CHECK(bs[stdx::type_identity_v<int>]);
bs.set<int>(false);
CHECK(not bs[stdx::type_identity_v<int>]);
}

TEST_CASE("reset single bit", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
CHECK(bs[stdx::type_identity_v<int>]);
bs.reset<int>();
CHECK(not bs[stdx::type_identity_v<int>]);
}

TEST_CASE("flip single bit", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
CHECK(bs[stdx::type_identity_v<int>]);
bs.flip<int>();
CHECK(not bs[stdx::type_identity_v<int>]);
}

TEST_CASE("construct with a value", "[type bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float>{1ul};
STATIC_CHECK(bs1[stdx::type_identity_v<int>]);

constexpr auto bs2 = stdx::type_bitset<int, float>{255ul};
STATIC_CHECK(bs2[stdx::type_identity_v<int>]);
STATIC_CHECK(bs2[stdx::type_identity_v<float>]);
}

TEST_CASE("construct with values for bits", "[type_bitset]") {
constexpr auto bs =
stdx::type_bitset<int, float, bool>{stdx::type_list<int, bool>{}};
STATIC_CHECK(bs[stdx::type_identity_v<int>]);
STATIC_CHECK(not bs[stdx::type_identity_v<float>]);
STATIC_CHECK(bs[stdx::type_identity_v<bool>]);
}

TEMPLATE_TEST_CASE("convert to unsigned integral type", "[type_bitset]",
std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t) {
constexpr auto bs = stdx::type_bitset<int, float, bool>{255ul};
constexpr auto val = bs.template to<TestType>();
STATIC_CHECK(std::is_same_v<decltype(val), TestType const>);
STATIC_CHECK(val == 7u);
}

TEST_CASE("convert to type that fits", "[type_bitset]") {
constexpr auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
constexpr auto val = bs.to_natural();
STATIC_CHECK(std::is_same_v<decltype(val), std::uint8_t const>);
STATIC_CHECK(val == 7u);
}

TEST_CASE("all", "[type bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(bs1.all());

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(not bs2.all());
}

TEST_CASE("any", "[type bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(bs1.any());

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(not bs2.any());
}

TEST_CASE("none", "[type bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(not bs1.none());

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(bs2.none());
}

TEST_CASE("count", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{};
STATIC_CHECK(bs1.count() == 0u);

constexpr auto bs2 = stdx::type_bitset<int, float, bool>{stdx::all_bits};
STATIC_CHECK(bs2.count() == 3u);
}

TEST_CASE("set all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{};
bs.set();
CHECK(bs == stdx::type_bitset<int, float, bool>{stdx::all_bits});
bs.set();
CHECK(bs == stdx::type_bitset<int, float, bool>{stdx::all_bits});
}

TEST_CASE("reset all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
bs.reset();
CHECK(bs == stdx::type_bitset<int, float, bool>{});
bs.reset();
CHECK(bs == stdx::type_bitset<int, float, bool>{});
}

TEST_CASE("flip all bits", "[type_bitset]") {
auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
bs.flip();
CHECK(bs == stdx::type_bitset<int, float, bool>{});
bs.flip();
CHECK(bs == stdx::type_bitset<int, float, bool>{stdx::all_bits});
}

TEST_CASE("or", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{0b101ul};
constexpr auto bs2 = stdx::type_bitset<int, float, bool>{0b010ul};
STATIC_CHECK((bs1 | bs2) ==
stdx::type_bitset<int, float, bool>{stdx::all_bits});
}

TEST_CASE("and", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{0b101ul};
constexpr auto bs2 = stdx::type_bitset<int, float, bool>{0b100ul};
STATIC_CHECK((bs1 & bs2) == stdx::type_bitset<int, float, bool>{0b100ul});
}

TEST_CASE("xor", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{0b101ul};
constexpr auto bs2 = stdx::type_bitset<int, float, bool>{0b010ul};
STATIC_CHECK((bs1 ^ bs2) ==
stdx::type_bitset<int, float, bool>{stdx::all_bits});
}

TEST_CASE("not", "[type_bitset]") {
constexpr auto bs = stdx::type_bitset<int, float, bool>{0b101ul};
STATIC_CHECK(~bs == stdx::type_bitset<int, float, bool>{0b10ul});
}

TEST_CASE("difference", "[type_bitset]") {
constexpr auto bs1 = stdx::type_bitset<int, float, bool>{0b101ul};
constexpr auto bs2 = stdx::type_bitset<int, float, bool>{0b011ul};
STATIC_CHECK((bs1 - bs2) == stdx::type_bitset<int, float, bool>{0b100ul});
}

#if __cplusplus >= 202002L
TEST_CASE("for_each", "[type_bitset]") {
constexpr auto bs = stdx::type_bitset<int, float, bool>{stdx::all_bits};
auto result = std::string{};
bs.for_each([&]<typename T, std::size_t I>() -> void {
result += std::string{stdx::type_as_string<T>()} + std::to_string(I);
});
CHECK(result == "int0float1bool2");
}
#endif