based

Opinionated 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

Diffstat:
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
{