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
83 changes: 83 additions & 0 deletions include/type_safe/flag_set.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,13 @@
import std;
#else
#include <climits>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <type_traits>
#endif

#include <type_safe/config.hpp>
#include <type_safe/flag.hpp>
#include <type_safe/types.hpp>

Expand Down Expand Up @@ -423,6 +426,72 @@ class flag_set
public:
using int_type = typename detail::flag_set_impl<Enum>::int_type;

class const_iterator
{
public:
using iterator_category = std::input_iterator_tag;
using value_type = Enum;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = Enum;

constexpr const_iterator() noexcept
: set_(nullptr), index_(flag_set_traits<Enum>::size())
{}

constexpr Enum operator*() const noexcept
{
return static_cast<Enum>(index_);
}

TYPE_SAFE_CONSTEXPR14 const_iterator& operator++() noexcept
{
++index_;
skip_unset();
return *this;
}

TYPE_SAFE_CONSTEXPR14 const_iterator operator++(int) noexcept
{
auto result = *this;
++*this;
return result;
}

constexpr bool operator==(const const_iterator& other) const noexcept
{
return set_ == other.set_ && index_ == other.index_;
}

constexpr bool operator!=(const const_iterator& other) const noexcept
{
return !(*this == other);
}

private:
constexpr const_iterator(const flag_set* set, std::size_t index) noexcept
: set_(set), index_(index)
{}

TYPE_SAFE_CONSTEXPR14 void skip_unset() noexcept
{
while (index_ < flag_set_traits<Enum>::size() && !is_set(index_))
++index_;
}

constexpr bool is_set(std::size_t index) const noexcept
{
return set_->is_set(static_cast<Enum>(index));
}

const flag_set* set_;
std::size_t index_;

friend class flag_set;
};

using iterator = const_iterator;

/// \returns a flag_set based on the given integer value.
/// \requires `T` must be of the same type as `int_type`.
template <typename T>
Expand Down Expand Up @@ -579,6 +648,20 @@ class flag_set
return flags_.to_int();
}

/// \returns An iterator to the first set flag.
const_iterator begin() const noexcept
{
auto result = const_iterator(this, 0u);
result.skip_unset();
return result;
}

/// \returns An iterator one past the last set flag.
constexpr const_iterator end() const noexcept
{
return const_iterator(this, flag_set_traits<Enum>::size());
}

//=== bitwise operations ===//
/// \returns A set with all the flags flipped.
constexpr flag_set operator~() const noexcept
Expand Down
48 changes: 48 additions & 0 deletions test/flag_set.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,52 @@ TEST_CASE("flag_set")
s ^= test_flags::a | test_flags::c;
check_set(s, true, false, false);
}
SECTION("iteration")
{
static_assert(std::is_same<decltype(*s.begin()), test_flags>::value, "iterator must return the flag enum");

test_flags flags[3] = {};
auto count = 0u;
for (auto flag : s)
flags[count++] = flag;
REQUIRE(count == 0u);

s.set(test_flags::b);
for (auto flag : s)
flags[count++] = flag;
REQUIRE(count == 1u);
REQUIRE(flags[0] == test_flags::b);

count = 0u;
s |= test_flags::a | test_flags::c;
for (auto flag : s)
flags[count++] = flag;
REQUIRE(count == 3u);
REQUIRE(flags[0] == test_flags::a);
REQUIRE(flags[1] == test_flags::b);
REQUIRE(flags[2] == test_flags::c);
}
SECTION("const iteration")
{
const auto c = set(test_flags::a | test_flags::c);
test_flags flags[2] = {};
auto count = 0u;
for (auto flag : c)
flags[count++] = flag;

REQUIRE(count == 2u);
REQUIRE(flags[0] == test_flags::a);
REQUIRE(flags[1] == test_flags::c);
}
SECTION("iteration ignores out of range bits")
{
auto raw = set::from_int<std::uint8_t>(0b11111000);
auto count = 0u;
for (auto flag : raw)
{
(void)flag;
++count;
}
REQUIRE(count == 0u);
}
}