From 288c2fc66b56ee1210ab0ea67f20f7bfd22a8a28 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 18 Dec 2020 01:57:42 +0700 Subject: [PATCH] first impl bitflags --- headers/enum.hpp/enum_bitflags.hpp | 284 +++++++++++++++++++++++++++++ untests/enum_bitflags_tests.cpp | 213 ++++++++++++++++++++++ 2 files changed, 497 insertions(+) create mode 100644 headers/enum.hpp/enum_bitflags.hpp create mode 100644 untests/enum_bitflags_tests.cpp diff --git a/headers/enum.hpp/enum_bitflags.hpp b/headers/enum.hpp/enum_bitflags.hpp new file mode 100644 index 0000000..bf4f15c --- /dev/null +++ b/headers/enum.hpp/enum_bitflags.hpp @@ -0,0 +1,284 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/enum.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019-2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include + +namespace enum_hpp::bitflags +{ + template < typename Enum > + class bitflags { + static_assert(std::is_enum_v); + public: + using enum_type = Enum; + using underlying_type = std::underlying_type_t; + + constexpr bitflags(enum_type flags) + : flags_(static_cast(flags)) {} + + constexpr explicit bitflags(underlying_type flags) + : flags_(flags) {} + + constexpr explicit operator bool() const noexcept { + return !!flags_; + } + + constexpr underlying_type as_raw() const noexcept { + return flags_; + } + + constexpr enum_type as_enum() const noexcept { + return static_cast(flags_); + } + private: + underlying_type flags_{}; + }; +} + +namespace enum_hpp::bitflags +{ + #define ENUM_HPP_DEFINE_BINARY_OPERATOR(op)\ + template < typename Enum >\ + constexpr bool operator op(Enum l, bitflags r) noexcept {\ + return l op r.as_raw();\ + }\ + template < typename Enum >\ + constexpr bool operator op(bitflags l, Enum r) noexcept {\ + return l.as_raw() op r;\ + }\ + template < typename Enum >\ + constexpr bool operator op(std::underlying_type_t l, bitflags r) noexcept {\ + return l op r.as_raw();\ + }\ + template < typename Enum >\ + constexpr bool operator op(bitflags l, std::underlying_type_t r) noexcept {\ + return l.as_raw() op r;\ + }\ + template < typename Enum >\ + constexpr bool operator op(bitflags l, bitflags r) noexcept {\ + return l.as_raw() op r.as_raw();\ + } + ENUM_HPP_DEFINE_BINARY_OPERATOR(<) + ENUM_HPP_DEFINE_BINARY_OPERATOR(>) + ENUM_HPP_DEFINE_BINARY_OPERATOR(<=) + ENUM_HPP_DEFINE_BINARY_OPERATOR(>=) + ENUM_HPP_DEFINE_BINARY_OPERATOR(==) + ENUM_HPP_DEFINE_BINARY_OPERATOR(!=) + #undef ENUM_HPP_DEFINE_BINARY_OPERATOR +} + +namespace enum_hpp::bitflags +{ + template < typename Enum > + constexpr bitflags operator~(bitflags l) noexcept { + return static_cast(~l.as_raw()); + } + + #define ENUM_HPP_DEFINE_BINARY_OPERATOR(op)\ + template < typename Enum >\ + constexpr bitflags operator op (Enum l, bitflags r) noexcept {\ + return bitflags{l} op r;\ + }\ + template < typename Enum >\ + constexpr bitflags operator op (bitflags l, Enum r) noexcept {\ + return l op bitflags{r};\ + }\ + template < typename Enum >\ + constexpr bitflags operator op (bitflags l, bitflags r) noexcept {\ + return static_cast(l.as_raw() op r.as_raw());\ + }\ + template < typename Enum >\ + constexpr bitflags& operator op##= (bitflags& l, Enum r) noexcept {\ + return l = l op bitflags{r};\ + }\ + template < typename Enum >\ + constexpr bitflags& operator op##= (bitflags& l, bitflags r) noexcept {\ + return l = l op r;\ + } + ENUM_HPP_DEFINE_BINARY_OPERATOR(|) + ENUM_HPP_DEFINE_BINARY_OPERATOR(&) + ENUM_HPP_DEFINE_BINARY_OPERATOR(^) + #undef ENUM_HPP_DEFINE_BINARY_OPERATOR +} + +namespace enum_hpp::bitflags +{ + // + // any + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool any(Enum flags) noexcept { + return any(bitflags{flags}); + } + + template < typename Enum > + constexpr bool any(bitflags flags) noexcept { + return 0 != flags.as_raw(); + } + + // + // none + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool none(Enum flags) noexcept { + return none(bitflags{flags}); + } + + template < typename Enum > + constexpr bool none(bitflags flags) noexcept { + return 0 == flags.as_raw(); + } + + // + // all_of + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool all_of(Enum l, Enum r) noexcept { + return all_of(bitflags{l}, bitflags{r}); + } + + template < typename Enum > + constexpr bool all_of(Enum l, bitflags r) noexcept { + return all_of(bitflags{l}, r); + } + + template < typename Enum > + constexpr bool all_of(bitflags l, Enum r) noexcept { + return all_of(l, bitflags{r}); + } + + template < typename Enum > + constexpr bool all_of(bitflags flags, bitflags mask) noexcept { + return (flags.as_raw() & mask.as_raw()) == mask.as_raw(); + } + + // + // any_of + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool any_of(Enum l, Enum r) noexcept { + return any_of(bitflags{l}, bitflags{r}); + } + + template < typename Enum > + constexpr bool any_of(Enum l, bitflags r) noexcept { + return any_of(bitflags{l}, r); + } + + template < typename Enum > + constexpr bool any_of(bitflags l, Enum r) noexcept { + return any_of(l, bitflags{r}); + } + + template < typename Enum > + constexpr bool any_of(bitflags l, bitflags r) noexcept { + return r.as_raw() == 0 + || (l.as_raw() & r.as_raw()) != 0; + } + + // + // none_of + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool none_of(Enum l, Enum r) noexcept { + return none_of(bitflags{l}, bitflags{r}); + } + + template < typename Enum > + constexpr bool none_of(Enum l, bitflags r) noexcept { + return none_of(bitflags{l}, r); + } + + template < typename Enum > + constexpr bool none_of(bitflags l, Enum r) noexcept { + return none_of(l, bitflags{r}); + } + + template < typename Enum > + constexpr bool none_of(bitflags l, bitflags r) noexcept { + return r.as_raw() != 0 + && (l.as_raw() & r.as_raw()) == 0; + } + + // + // any_except + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool any_except(Enum l, Enum r) noexcept { + return any_except(bitflags{l}, bitflags{r}); + } + + template < typename Enum > + constexpr bool any_except(Enum l, bitflags r) noexcept { + return any_except(bitflags{l}, r); + } + + template < typename Enum > + constexpr bool any_except(bitflags l, Enum r) noexcept { + return any_except(l, bitflags{r}); + } + + template < typename Enum > + constexpr bool any_except(bitflags l, bitflags r) noexcept { + return any_of(l, ~r); + } + + // + // none_except + // + + template < typename Enum + , std::enable_if_t, int> = 0 > + constexpr bool none_except(Enum l, Enum r) noexcept { + return none_except(bitflags{l}, bitflags{r}); + } + + template < typename Enum > + constexpr bool none_except(Enum l, bitflags r) noexcept { + return none_except(bitflags{l}, r); + } + + template < typename Enum > + constexpr bool none_except(bitflags l, Enum r) noexcept { + return none_except(l, bitflags{r}); + } + + template < typename Enum > + constexpr bool none_except(bitflags l, bitflags r) noexcept { + return none_of(l, ~r); + } +} + +// +// ENUM_HPP_REGISTER_BITFLAGS_OPERATORS +// + +#define ENUM_HPP_REGISTER_BITFLAGS_OPERATORS(Enum)\ + constexpr ::enum_hpp::bitflags::bitflags operator~ [[maybe_unused]] (Enum l) noexcept {\ + return ~::enum_hpp::bitflags::bitflags(l);\ + }\ + constexpr ::enum_hpp::bitflags::bitflags operator| [[maybe_unused]] (Enum l, Enum r) noexcept {\ + return ::enum_hpp::bitflags::bitflags(l) | ::enum_hpp::bitflags::bitflags(r);\ + }\ + constexpr ::enum_hpp::bitflags::bitflags operator& [[maybe_unused]] (Enum l, Enum r) noexcept {\ + return ::enum_hpp::bitflags::bitflags(l) & ::enum_hpp::bitflags::bitflags(r);\ + }\ + constexpr ::enum_hpp::bitflags::bitflags operator^ [[maybe_unused]] (Enum l, Enum r) noexcept {\ + return ::enum_hpp::bitflags::bitflags(l) ^ ::enum_hpp::bitflags::bitflags(r);\ + } diff --git a/untests/enum_bitflags_tests.cpp b/untests/enum_bitflags_tests.cpp new file mode 100644 index 0000000..e6d271d --- /dev/null +++ b/untests/enum_bitflags_tests.cpp @@ -0,0 +1,213 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/enum.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2019-2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include + +#include "doctest/doctest.hpp" + +#include +#include + +namespace +{ + enum class access : std::uint8_t { + none = 0, + read = 1 << 0, + write = 1 << 1, + read_write = read | write, + }; + + ENUM_HPP_REGISTER_BITFLAGS_OPERATORS(access) +} + +TEST_CASE("enum_bitflags") { + namespace bf = enum_hpp::bitflags; + + SUBCASE("enum_operators") { + STATIC_CHECK((~access::read) == bf::bitflags(0xFE)); + STATIC_CHECK((access::read | access::write) == bf::bitflags(0x3)); + STATIC_CHECK((access::read_write & access::write) == bf::bitflags(0x2)); + STATIC_CHECK((access::read_write ^ access::write) == bf::bitflags(0x1)); + } + + SUBCASE("bitflags_operators") { + STATIC_CHECK(0xFE == ~bf::bitflags{access::read}); + STATIC_CHECK(~bf::bitflags{access::read} == 0xFE); + STATIC_CHECK(~bf::bitflags{access::read} == bf::bitflags(0xFE)); + + STATIC_CHECK((access::write | bf::bitflags{access::read}) == 0x3); + STATIC_CHECK((bf::bitflags{access::read} | access::write) == 0x3); + STATIC_CHECK((bf::bitflags{access::read} | bf::bitflags{access::write}) == 0x3); + + STATIC_CHECK((access::write & bf::bitflags{access::read_write}) == 0x2); + STATIC_CHECK((bf::bitflags{access::read_write} & access::write) == 0x2); + STATIC_CHECK((bf::bitflags{access::read_write} & bf::bitflags{access::write}) == 0x2); + + STATIC_CHECK((access::write ^ bf::bitflags{access::read_write}) == 0x1); + STATIC_CHECK((bf::bitflags{access::read_write} ^ access::write) == 0x1); + STATIC_CHECK((bf::bitflags{access::read_write} ^ bf::bitflags{access::write}) == 0x1); + + { + bf::bitflags f{access::read}; + f |= access::write; + CHECK(f == 0x3); + } + + { + bf::bitflags f{access::read}; + f |= bf::bitflags{access::write}; + CHECK(f == 0x3); + } + + { + bf::bitflags f{access::read_write}; + f &= access::write; + CHECK(f == 0x2); + } + + { + bf::bitflags f{access::read_write}; + f &= bf::bitflags{access::write}; + CHECK(f == 0x2); + } + + { + bf::bitflags f{access::read_write}; + f ^= access::write; + CHECK(f == 0x1); + } + + { + bf::bitflags f{access::read_write}; + f ^= bf::bitflags{access::write}; + CHECK(f == 0x1); + } + } + + SUBCASE("any") { + STATIC_CHECK_FALSE(bf::any(access::none)); + STATIC_CHECK(bf::any(access::read)); + STATIC_CHECK(bf::any(access::write)); + STATIC_CHECK(bf::any(access::read_write)); + } + + SUBCASE("none") { + STATIC_CHECK(bf::none(access::none)); + STATIC_CHECK_FALSE(bf::none(access::read)); + STATIC_CHECK_FALSE(bf::none(access::write)); + STATIC_CHECK_FALSE(bf::none(access::read_write)); + } + + SUBCASE("all_of") { + STATIC_CHECK(bf::all_of(access::none, access::none)); + STATIC_CHECK_FALSE(bf::all_of(access::none, access::read)); + STATIC_CHECK_FALSE(bf::all_of(access::none, access::write)); + STATIC_CHECK_FALSE(bf::all_of(access::none, access::read_write)); + + STATIC_CHECK(bf::all_of(access::read, access::none)); + STATIC_CHECK(bf::all_of(access::read, access::read)); + STATIC_CHECK_FALSE(bf::all_of(access::read, access::write)); + STATIC_CHECK_FALSE(bf::all_of(access::read, access::read_write)); + + STATIC_CHECK(bf::all_of(access::write, access::none)); + STATIC_CHECK_FALSE(bf::all_of(access::write, access::read)); + STATIC_CHECK(bf::all_of(access::write, access::write)); + STATIC_CHECK_FALSE(bf::all_of(access::write, access::read_write)); + + STATIC_CHECK(bf::all_of(access::read_write, access::none)); + STATIC_CHECK(bf::all_of(access::read_write, access::read)); + STATIC_CHECK(bf::all_of(access::read_write, access::write)); + STATIC_CHECK(bf::all_of(access::read_write, access::read_write)); + } + + SUBCASE("any_of") { + STATIC_CHECK(bf::any_of(access::none, access::none)); + STATIC_CHECK_FALSE(bf::any_of(access::none, access::read)); + STATIC_CHECK_FALSE(bf::any_of(access::none, access::write)); + STATIC_CHECK_FALSE(bf::any_of(access::none, access::read_write)); + + STATIC_CHECK(bf::any_of(access::read, access::none)); + STATIC_CHECK(bf::any_of(access::read, access::read)); + STATIC_CHECK_FALSE(bf::any_of(access::read, access::write)); + STATIC_CHECK(bf::any_of(access::read, access::read_write)); + + STATIC_CHECK(bf::any_of(access::write, access::none)); + STATIC_CHECK_FALSE(bf::any_of(access::write, access::read)); + STATIC_CHECK(bf::any_of(access::write, access::write)); + STATIC_CHECK(bf::any_of(access::write, access::read_write)); + + STATIC_CHECK(bf::any_of(access::read_write, access::none)); + STATIC_CHECK(bf::any_of(access::read_write, access::read)); + STATIC_CHECK(bf::any_of(access::read_write, access::write)); + STATIC_CHECK(bf::any_of(access::read_write, access::read_write)); + } + + SUBCASE("none_of") { + STATIC_CHECK_FALSE(bf::none_of(access::none, access::none)); + STATIC_CHECK(bf::none_of(access::none, access::read)); + STATIC_CHECK(bf::none_of(access::none, access::write)); + STATIC_CHECK(bf::none_of(access::none, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_of(access::read, access::none)); + STATIC_CHECK_FALSE(bf::none_of(access::read, access::read)); + STATIC_CHECK(bf::none_of(access::read, access::write)); + STATIC_CHECK_FALSE(bf::none_of(access::read, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_of(access::write, access::none)); + STATIC_CHECK(bf::none_of(access::write, access::read)); + STATIC_CHECK_FALSE(bf::none_of(access::write, access::write)); + STATIC_CHECK_FALSE(bf::none_of(access::write, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_of(access::read_write, access::none)); + STATIC_CHECK_FALSE(bf::none_of(access::read_write, access::read)); + STATIC_CHECK_FALSE(bf::none_of(access::read_write, access::write)); + STATIC_CHECK_FALSE(bf::none_of(access::read_write, access::read_write)); + } + + SUBCASE("any_except") { + STATIC_CHECK_FALSE(bf::any_except(access::none, access::none)); + STATIC_CHECK_FALSE(bf::any_except(access::none, access::read)); + STATIC_CHECK_FALSE(bf::any_except(access::none, access::write)); + STATIC_CHECK_FALSE(bf::any_except(access::none, access::read_write)); + + STATIC_CHECK(bf::any_except(access::read, access::none)); + STATIC_CHECK_FALSE(bf::any_except(access::read, access::read)); + STATIC_CHECK(bf::any_except(access::read, access::write)); + STATIC_CHECK_FALSE(bf::any_except(access::read, access::read_write)); + + STATIC_CHECK(bf::any_except(access::write, access::none)); + STATIC_CHECK(bf::any_except(access::write, access::read)); + STATIC_CHECK_FALSE(bf::any_except(access::write, access::write)); + STATIC_CHECK_FALSE(bf::any_except(access::write, access::read_write)); + + STATIC_CHECK(bf::any_except(access::read_write, access::none)); + STATIC_CHECK(bf::any_except(access::read_write, access::read)); + STATIC_CHECK(bf::any_except(access::read_write, access::write)); + STATIC_CHECK_FALSE(bf::any_except(access::read_write, access::read_write)); + } + + SUBCASE("none_except") { + STATIC_CHECK(bf::none_except(access::none, access::none)); + STATIC_CHECK(bf::none_except(access::none, access::read)); + STATIC_CHECK(bf::none_except(access::none, access::write)); + STATIC_CHECK(bf::none_except(access::none, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_except(access::read, access::none)); + STATIC_CHECK(bf::none_except(access::read, access::read)); + STATIC_CHECK_FALSE(bf::none_except(access::read, access::write)); + STATIC_CHECK(bf::none_except(access::read, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_except(access::write, access::none)); + STATIC_CHECK_FALSE(bf::none_except(access::write, access::read)); + STATIC_CHECK(bf::none_except(access::write, access::write)); + STATIC_CHECK(bf::none_except(access::write, access::read_write)); + + STATIC_CHECK_FALSE(bf::none_except(access::read_write, access::none)); + STATIC_CHECK_FALSE(bf::none_except(access::read_write, access::read)); + STATIC_CHECK_FALSE(bf::none_except(access::read_write, access::write)); + STATIC_CHECK(bf::none_except(access::read_write, access::read_write)); + } +}