basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 042d328c9c2924b0c1490bbe45e3c5695b761f7b |
parent | 5769377617f46869ceef0cdf1fb807eed3cb58c7 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Thu, 8 May 2025 18:01:14 +0200 |
Add enum_flag
M | include/based/enum/enum.hpp | | | ++ -- |
A | include/based/enum/enum_flag.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | include/based/instrumentation/instrumented.hpp | | | + - |
M | test/CMakeLists.txt | | | + |
A | test/source/enum_flag_test.cpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | test/source/enum_test.cpp | | | + - |
6 files changed, 222 insertions(+), 4 deletions(-)
diff --git a/ include/based/enum/enum.hpp b/ include/based/enum/enum.hpp
@@ -127,10 +127,10 @@
BASED_FOREACH(BASED_DETAIL_ENUM_DECLARE_VAL, __VA_ARGS__) \
};
#define BASED_ENUM_DEFINE(Name, Type, ...) \
#define BASED_DEFINE_ENUM(Name, Type, ...) \
BASED_DETAIL_ENUM_DEFINE(Name, Type, __VA_ARGS__)
#define BASED_DEFINE_CLASS_ENUM(Class, Name, Type, ...) \
#define BASED_DEFINE_ENUM_CLASS(Class, Name, Type, ...) \
BASED_DETAIL_ENUM_DEFINE(Class::Name, Type, __VA_ARGS__)
// NOLINTEND(*macro-usage*)
diff --git a/ include/based/enum/enum_flag.hpp b/ include/based/enum/enum_flag.hpp
@@ -0,0 +1,151 @@
#pragma once
#include <cassert>
#include "based/macro/macros.hpp"
// NOLINTBEGIN(*macro-usage*)
#define BASED_LIST_ELEM_STR(Name, Index) #Name,
#define BASED_LIST_STR(...) BASED_FOREACH(BASED_LIST_ELEM_STR, __VA_ARGS__)
// NOLINTNEXTLINE(*macro-parentheses*)
#define BASED_SET(var, val) decltype(var) var = decltype(var) {val};
#define BASED_DETAIL_ENUM_DECLARE_VAL(Name, Index) static const type Name;
#define BASED_DETAIL_ENUM_DECLARE_CASE(Qualifier, Name, Index) \
case Qualifier::Name.value: \
return Name;
#define BASED_DETAIL_ENUM_DEFINE_FLAG_VAL(Qualifier, Name, Index) \
inline constexpr BASED_SET( \
Qualifier::Name, \
Qualifier::type::size_t {1} << Qualifier::type::size_t {(Index)} \
)
#define BASED_DETAIL_ENUM_DEFINE_VALS(Qualifier, ...) \
BASED_FOREACH_1(Qualifier, BASED_DETAIL_ENUM_DEFINE_FLAG_VAL, __VA_ARGS__)
#define BASED_DETAIL_ENUM_DEFINE_GET(Qualifier, Type, ...) \
inline const Qualifier::type& Qualifier::type::get(Type idx) \
{ \
/* NOLINTNEXTLINE(*paths-covered*) */ \
switch (idx) { \
BASED_FOREACH_1(Qualifier, BASED_DETAIL_ENUM_DECLARE_CASE, __VA_ARGS__) \
default: \
break; \
} \
assert(0); /* NOLINT(*assert*,cert-dcl03-c) */ \
}
#define BASED_DETAIL_ENUM_DEFINE(Qualifier, Type, ...) \
BASED_DETAIL_ENUM_DEFINE_VALS(Qualifier, __VA_ARGS__) \
BASED_DETAIL_ENUM_DEFINE_GET(Qualifier, Type, __VA_ARGS__)
#define BASED_ENUM_DECLARE_FLAG(Name, Type, ...) \
struct Name \
{ \
class type \
{ \
friend Name; \
\
constexpr explicit type(Type enum_value) \
: value(enum_value) \
{ \
} \
\
public: \
using size_t = Type; \
static constexpr size_t size = \
BASED_NUMARGS(BASED_LIST_STR(__VA_ARGS__)); \
\
static const type& get(Type idx); \
\
type& set(type val) \
{ \
return *this |= val; \
} \
\
type& mask(type val) \
{ \
return *this &= val; \
} \
\
type& tgl(type val) \
{ \
return *this ^= val; \
} \
\
type& neg() \
{ \
return *this = ~*this; \
} \
\
type& clear(type val) \
{ \
return *this &= ~val; \
} \
\
bool test(type val) const \
{ \
return (*this & val) == val; \
} \
\
friend bool operator==(type lhs, type rhs) \
{ \
return lhs.value == rhs.value; \
} \
\
friend type operator|(type lhs, type rhs) \
{ \
return type(lhs.value | rhs.value); \
} \
\
friend type operator&(type lhs, type rhs) \
{ \
return type(lhs.value & rhs.value); \
} \
\
friend type operator^(type lhs, type rhs) \
{ \
return type(lhs.value ^ rhs.value); \
} \
\
type operator~() const \
{ \
return type(~value); \
} \
\
type& operator|=(type rhs) \
{ \
value |= rhs.value; \
return *this; \
} \
\
type& operator&=(type rhs) \
{ \
value &= rhs.value; \
return *this; \
} \
\
type& operator^=(type rhs) \
{ \
value ^= rhs.value; \
return *this; \
} \
\
Type value; \
}; \
\
BASED_FOREACH(BASED_DETAIL_ENUM_DECLARE_VAL, __VA_ARGS__) \
};
#define BASED_DEFINE_ENUM_FLAG(Name, Type, ...) \
BASED_DETAIL_ENUM_DEFINE(Name, Type, __VA_ARGS__)
#define BASED_DEFINE_ENUM_FLAG_CLASS(Class, Name, Type, ...) \
BASED_DETAIL_ENUM_DEFINE(Class::Name, Type, __VA_ARGS__)
// NOLINTEND(*macro-usage*)
diff --git a/ include/based/instrumentation/instrumented.hpp b/ include/based/instrumentation/instrumented.hpp
@@ -41,7 +41,7 @@
struct instrumented_base
}
};
BASED_DEFINE_CLASS_ENUM(
BASED_DEFINE_ENUM_CLASS(
instrumented_base,
op,
u8,
diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -69,6 +69,7 @@
add_test(functional function_test)
## ------ Enum -----
add_test(. enum_test)
add_test(. enum_flag_test)
## ----- List -----
diff --git a/ test/source/enum_flag_test.cpp b/ test/source/enum_flag_test.cpp
@@ -0,0 +1,66 @@
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include "based/enum/enum_flag.hpp"
#include <catch2/catch_test_macros.hpp>
#include "based/concepts/comparable/equality.hpp"
#include "based/concepts/comparable/greater.hpp"
#include "based/concepts/comparable/greater_equal.hpp"
#include "based/concepts/comparable/less.hpp"
#include "based/concepts/comparable/less_equal.hpp"
#include "based/concepts/is/same.hpp"
#include "based/types/types.hpp"
BASED_ENUM_DECLARE_FLAG(var, based::u8, a, b, c)
BASED_DEFINE_ENUM_FLAG(var, based::u8, a, b, c)
TEST_CASE("types", "[enum/enum]")
{
STATIC_REQUIRE(requires { typename var; });
STATIC_REQUIRE(requires { var::type::size == 3; });
STATIC_REQUIRE(requires { var::a; });
STATIC_REQUIRE(requires { var::b; });
STATIC_REQUIRE(requires { var::c; });
}
TEST_CASE("operations", "[enum/enum]")
{
using based::SameAs;
SECTION("COMPARE")
{
STATIC_REQUIRE(based::EqualityComparable<var::type>);
STATIC_REQUIRE(!based::LessComparable<var::type>);
STATIC_REQUIRE(!based::GreaterComparable<var::type>);
STATIC_REQUIRE(!based::LessEqualComparable<var::type>);
STATIC_REQUIRE(!based::GreaterEqualComparable<var::type>);
}
SECTION("functions")
{
const auto var1 = var::a | var::b;
REQUIRE(var1.test(var::a));
REQUIRE(var1.test(var::b));
REQUIRE(!var1.test(var::c));
const auto var2 = ~var1;
REQUIRE(!var2.test(var::a));
REQUIRE(!var2.test(var::b));
REQUIRE(var2.test(var::c));
const auto var3 = var1 & var2;
REQUIRE(!var3.test(var::a));
REQUIRE(!var3.test(var::b));
REQUIRE(!var3.test(var::c));
const auto var4 = var1 ^ var2;
REQUIRE(var4.test(var::a));
REQUIRE(var4.test(var::b));
REQUIRE(var4.test(var::c));
}
}
diff --git a/ test/source/enum_test.cpp b/ test/source/enum_test.cpp
@@ -25,7 +25,7 @@
private:
int m_c = 3;
};
BASED_DEFINE_CLASS_ENUM(test, var, based::u8, a, b, c)
BASED_DEFINE_ENUM_CLASS(test, var, based::u8, a, b, c)
inline int test::get_var(var::type req) const
{