diff --git a/include/flatmemory/details/concepts.hpp b/include/flatmemory/details/concepts.hpp index 0290fd4..9510756 100644 --- a/include/flatmemory/details/concepts.hpp +++ b/include/flatmemory/details/concepts.hpp @@ -26,6 +26,10 @@ namespace flatmemory // Concept to check whether T is integral template concept IsIntegral = std::is_integral_v; + +// Concept to check whether T is an unsigned integral +template +concept IsUnsignedIntegral = IsIntegral && std::is_unsigned_v; } #endif diff --git a/include/flatmemory/details/types/bitset.hpp b/include/flatmemory/details/types/bitset.hpp index f1d79fe..20a6de5 100644 --- a/include/flatmemory/details/types/bitset.hpp +++ b/include/flatmemory/details/types/bitset.hpp @@ -41,8 +41,10 @@ namespace flatmemory { /** * Dispatcher for Bitset. + * The optional tag can be used to disallow operations between bitsets have similar tag. */ -template + +template struct Bitset : public Custom { /// @brief Non-trivial copy-constructor @@ -53,8 +55,9 @@ struct Bitset : public Custom /** * Layout */ -template -class Layout> + +template +class Layout> { public: static constexpr size_t final_alignment = calculate_final_alignment>(); @@ -83,28 +86,43 @@ class Layout> /** * Forward declarations */ -template -class View>; -template -class ConstView>; +template +class View>; -template -class Builder>; +template +class ConstView>; + +template +class Builder>; /** * Concepts */ -// Concept to check if two types have the same Block type +/// @brief Concept to check if two types `T1` and `T2` have the same Block type. template concept HaveSameBlockType = std::is_same_v; -// Concept to check the block type +/// @brief Concept to check if two types have a compatible `Tag`, +/// i.e., either one is void or both are the same. +template +concept HaveCompatibleTagType = + std::is_same_v || std::is_same_v || std::is_same_v; + +/// @brief Concept to check the `Block` type of another bitset +/// is compatible with the block type of the bitset of type `T`, i.e., +/// the block types are the same. template concept HasBlockType = std::is_same_v; -// Concept for user define bitsets based on the STL +/// @brief Concept to check whether the `Tag` type of another bitset +/// is compatible with the tag type of the bitset of type `T`, +/// i.e., either one is void or both are the same. +template +concept HasCompatibleTagType = std::is_same_v || std::is_same_v || std::is_same_v; + +/// @brief Concept for user define bitsets based on the STL template concept IsUserDefinedBitset = requires(T a, const T b) { /* Common */ @@ -130,12 +148,13 @@ concept IsUserDefinedBitset = requires(T a, const T b) { } -> std::same_as&>; }; -// Concept for Builder +/// @brief Concept for Builder template concept IsBitsetBuilder = requires(T a, const T b) { /* Common */ requires IsIntegral; + typename T::TagType; /* Non const version*/ @@ -156,12 +175,13 @@ concept IsBitsetBuilder = requires(T a, const T b) { } -> std::same_as>&>; }; -// Concept for View +/// @brief Concept for View template concept IsBitsetView = requires(T a, const T b) { /* Common */ requires IsIntegral; + typename T::TagType; /* Non const version*/ @@ -182,11 +202,13 @@ concept IsBitsetView = requires(T a, const T b) { } -> std::same_as>>; }; -// Concept for ConstView +/// @brief Concept for ConstView template concept IsBitsetConstView = requires(T a, const T b) { /* Common */ + requires IsIntegral; + typename T::TagType; /* Non const version*/ @@ -207,15 +229,15 @@ concept IsBitsetConstView = requires(T a, const T b) { } -> std::same_as>>; }; -// Concept for Bitset +/// @brief Concept for Bitset template concept IsBitset = IsUserDefinedBitset || IsBitsetBuilder || IsBitsetView || IsBitsetConstView; /** * Operator */ -template -class Operator> +template +class Operator> { public: static constexpr std::size_t block_size = sizeof(Block) * 8; @@ -234,7 +256,7 @@ class Operator> * Operators */ template - requires HaveSameBlockType + requires HaveSameBlockType && HaveCompatibleTagType static bool are_equal(const L& left_bitset, const R& right_bitset) { // Fetch data @@ -264,7 +286,7 @@ class Operator> } template - requires HaveSameBlockType + requires HaveSameBlockType && HaveCompatibleTagType static bool less(const L& left_bitset, const R& right_bitset) { // Fetch data @@ -300,7 +322,7 @@ class Operator> } template - requires HaveSameBlockType + requires HaveSameBlockType && HaveCompatibleTagType static bool is_superseteq(const L& left_bitset, const R& right_bitset) { // Fetch data @@ -351,7 +373,7 @@ class Operator> } template - requires HaveSameBlockType + requires HaveSameBlockType && HaveCompatibleTagType static bool are_disjoint(const L& left_bitset, const R& right_bitset) { // Fetch data @@ -627,11 +649,14 @@ class Operator> /** * View */ -template -class View> +template +class View> { private: - using BitsetOperator = Operator>; + using BitsetLayout = Layout>; + using BitsetOperator = Operator>; + using BitsetView = View>; + using BitsetConstView = ConstView>; using const_iterator = typename BitsetOperator::const_iterator; uint8_t* m_buf; @@ -646,6 +671,7 @@ class View> public: using BlockType = Block; + using TagType = Tag; /** * Constructor to interpret raw data created by its corresponding builder @@ -657,7 +683,7 @@ class View> */ template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType [[nodiscard]] bool operator==(const Other& other) const { assert(m_buf); @@ -665,14 +691,14 @@ class View> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType [[nodiscard]] bool operator!=(const Other& other) const { return !(*this == other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool is_superseteq(const Other& other) const { assert(m_buf); @@ -680,7 +706,7 @@ class View> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool are_disjoint(const Other& other) const { assert(m_buf); @@ -731,45 +757,48 @@ class View> [[nodiscard]] buffer_size_type buffer_size() const { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::buffer_size_position)); - return read_value(m_buf + Layout>::buffer_size_position); + assert(test_correct_alignment(m_buf + BitsetLayout::buffer_size_position)); + return read_value(m_buf + BitsetLayout::buffer_size_position); } [[nodiscard]] bool& get_default_bit_value() { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::default_bit_value_position)); - return read_value(m_buf + Layout>::default_bit_value_position); + assert(test_correct_alignment(m_buf + BitsetLayout::default_bit_value_position)); + return read_value(m_buf + BitsetLayout::default_bit_value_position); } [[nodiscard]] bool get_default_bit_value() const { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::default_bit_value_position)); - return read_value(m_buf + Layout>::default_bit_value_position); + assert(test_correct_alignment(m_buf + BitsetLayout::default_bit_value_position)); + return read_value(m_buf + BitsetLayout::default_bit_value_position); } [[nodiscard]] View> get_blocks() { assert(m_buf); - return View>(m_buf + Layout>::blocks_position); + return View>(m_buf + BitsetLayout::blocks_position); } [[nodiscard]] ConstView> get_blocks() const { assert(m_buf); - return ConstView>(m_buf + Layout>::blocks_position); + return ConstView>(m_buf + BitsetLayout::blocks_position); } }; /** * ConstView */ -template -class ConstView> +template +class ConstView> { private: - using BitsetOperator = Operator>; + using BitsetLayout = Layout>; + using BitsetOperator = Operator>; + using BitsetView = View>; + using BitsetConstView = ConstView>; using const_iterator = typename BitsetOperator::const_iterator; const uint8_t* m_buf; @@ -784,6 +813,7 @@ class ConstView> public: using BlockType = Block; + using TagType = Tag; /** * Constructor to interpret raw data created by its corresponding builder @@ -793,14 +823,14 @@ class ConstView> /** * Conversion constructor */ - ConstView(const View>& view) : m_buf(view.buffer()) {} + ConstView(const BitsetView& view) : m_buf(view.buffer()) {} /** * Operators */ template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType [[nodiscard]] bool operator==(const Other& other) const { assert(m_buf); @@ -808,14 +838,14 @@ class ConstView> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType [[nodiscard]] bool operator!=(const Other& other) const { return !(*this == other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool is_superseteq(const Other& other) const { assert(m_buf); @@ -823,7 +853,7 @@ class ConstView> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool are_disjoint(const Other& other) const { assert(m_buf); @@ -874,45 +904,48 @@ class ConstView> [[nodiscard]] buffer_size_type buffer_size() const { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::buffer_size_position)); - return read_value(m_buf + Layout>::buffer_size_position); + assert(test_correct_alignment(m_buf + BitsetLayout::buffer_size_position)); + return read_value(m_buf + BitsetLayout::buffer_size_position); } [[nodiscard]] bool get_default_bit_value() { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::default_bit_value_position)); - return read_value(m_buf + Layout>::default_bit_value_position); + assert(test_correct_alignment(m_buf + BitsetLayout::default_bit_value_position)); + return read_value(m_buf + BitsetLayout::default_bit_value_position); } [[nodiscard]] bool get_default_bit_value() const { assert(m_buf); - assert(test_correct_alignment(m_buf + Layout>::default_bit_value_position)); - return read_value(m_buf + Layout>::default_bit_value_position); + assert(test_correct_alignment(m_buf + BitsetLayout::default_bit_value_position)); + return read_value(m_buf + BitsetLayout::default_bit_value_position); } [[nodiscard]] ConstView> get_blocks() { assert(m_buf); - return ConstView>(m_buf + Layout>::blocks_position); + return ConstView>(m_buf + BitsetLayout::blocks_position); } [[nodiscard]] ConstView> get_blocks() const { assert(m_buf); - return ConstView>(m_buf + Layout>::blocks_position); + return ConstView>(m_buf + BitsetLayout::blocks_position); } }; /** * Builder */ -template -class Builder> : public IBuilder>> +template +class Builder> : public IBuilder>> { private: - using BitsetOperator = Operator>; + using BitsetOperator = Operator>; + using BitsetLayout = Layout>; + using BitsetView = View>; + using BitsetConstView = ConstView>; using const_iterator = typename BitsetOperator::const_iterator; bool m_default_bit_value; @@ -928,20 +961,20 @@ class Builder> : public IBuilder>> { /* Write header info */ // Write default_bit_value - m_buffer.write(Layout>::default_bit_value_position, m_default_bit_value); - m_buffer.write_padding(Layout>::default_bit_value_end, Layout>::default_bit_value_padding); + m_buffer.write(BitsetLayout::default_bit_value_position, m_default_bit_value); + m_buffer.write_padding(BitsetLayout::default_bit_value_end, BitsetLayout::default_bit_value_padding); /* Write dynamic info */ - buffer_size_type buffer_size = Layout>::blocks_position; + buffer_size_type buffer_size = BitsetLayout::blocks_position; // Write blocks m_blocks.finish(); - buffer_size += m_buffer.write(Layout>::blocks_position, m_blocks.buffer().data(), m_blocks.buffer().size()); + buffer_size += m_buffer.write(BitsetLayout::blocks_position, m_blocks.buffer().data(), m_blocks.buffer().size()); // Write final padding - buffer_size += m_buffer.write_padding(buffer_size, calculate_amount_padding(buffer_size, Layout>::final_alignment)); + buffer_size += m_buffer.write_padding(buffer_size, calculate_amount_padding(buffer_size, BitsetLayout::final_alignment)); /* Write buffer size */ - m_buffer.write(Layout>::buffer_size_position, buffer_size); + m_buffer.write(BitsetLayout::buffer_size_position, buffer_size); m_buffer.set_size(buffer_size); } @@ -949,6 +982,7 @@ class Builder> : public IBuilder>> [[nodiscard]] const auto& get_buffer_impl() const { return m_buffer; } template + requires HasBlockType && HasCompatibleTagType void resize_to_fit(const Other& other) { if (m_blocks.size() < other.get_blocks().size()) @@ -959,9 +993,9 @@ class Builder> : public IBuilder>> assert(m_blocks.size() > 0); } - template - requires HasBlockType - void init_from_view(const T& other) + template + requires HasBlockType && HasCompatibleTagType + void init_from_view(const Other& other) { m_default_bit_value = other.get_default_bit_value(); const auto& other_blocks = other.get_blocks(); @@ -974,6 +1008,7 @@ class Builder> : public IBuilder>> public: using BlockType = Block; + using TagType = Tag; /** * Constructors @@ -999,21 +1034,21 @@ class Builder> : public IBuilder>> * Conversion constructors */ - Builder(const View>& other) { init_from_view(other); } + Builder(const BitsetView& other) { init_from_view(other); } - Builder(const ConstView>& other) { init_from_view(other); } + Builder(const BitsetConstView& other) { init_from_view(other); } /** * Conversion assignments */ - Builder& operator=(const View>& other) + Builder& operator=(const BitsetView& other) { init_from_view(other); return *this; } - Builder& operator=(const ConstView>& other) + Builder& operator=(const BitsetConstView& other) { init_from_view(other); return *this; @@ -1024,35 +1059,35 @@ class Builder> : public IBuilder>> */ template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool operator<(const Other& other) const { return BitsetOperator::less(*this, other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool operator==(const Other& other) const { return BitsetOperator::are_equal(*this, other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType [[nodiscard]] bool operator!=(const Other& other) const { return !(*this == other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool is_superseteq(const Other& other) const { return BitsetOperator::is_superseteq(*this, other); } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType bool are_disjoint(const Other& other) const { return BitsetOperator::are_disjoint(*this, other); @@ -1141,7 +1176,7 @@ class Builder> : public IBuilder>> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType Builder& operator|=(const Other& other) { // Fetch data @@ -1170,7 +1205,7 @@ class Builder> : public IBuilder>> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType Builder& operator&=(const Other& other) { // Fetch data @@ -1200,7 +1235,7 @@ class Builder> : public IBuilder>> } template - requires HasBlockType + requires HasBlockType && HasCompatibleTagType Builder& operator-=(const Other& other) { // Fetch data @@ -1306,6 +1341,24 @@ class Builder> : public IBuilder>> static_assert(std::ranges::forward_range>>); static_assert(std::ranges::forward_range>>); static_assert(std::ranges::forward_range>>); + +static_assert(HaveSameBlockType>, Builder>>); +static_assert(!HaveSameBlockType>, Builder>>); + +static_assert(HaveSameBlockType>, View>>); +static_assert(!HaveSameBlockType>, View>>); + +struct Tag1 +{ +}; +struct Tag2 +{ +}; +static_assert(HaveCompatibleTagType>, Builder>>); +static_assert(!HaveCompatibleTagType>, Builder>>); + +static_assert(HaveCompatibleTagType>, View>>); +static_assert(!HaveCompatibleTagType>, View>>); } #endif