based

Opinionated utility library
git clone git://git.dimitrijedobrota.com/based.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commit f32ec36db38b2d1fa203da7f0f850c3c9dc05e96
parent cea76b76ae0f57fd5dbd4a035948757971eeebcd
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Tue, 17 Jun 2025 01:11:46 +0200

Enums should work with strong integers

Diffstat:
M include/based/enum/enum.hpp | ++++++++++++ -----------
M include/based/enum/enum_flag.hpp | +++++++++++++++++ ----------
M test/source/enum_flag_test.cpp | ++++++++++++ ----------
M test/source/enum_test.cpp | +++++++ ----

4 files changed, 48 insertions(+), 35 deletions(-)


diff --git a/ include/based/enum/enum.hpp b/ include/based/enum/enum.hpp

@@ -1,16 +1,16 @@

#pragma once

#include <array>
#include <cassert>

#include "based/assert.hpp"
#include "based/container/array.hpp"
#include "based/macro/foreach.hpp"
#include "based/macro/foreach_1.hpp"
#include "based/macro/foreach_2.hpp"
#include "based/types/types.hpp"
#include "based/utility/forward.hpp"

// NOLINTBEGIN(*macro-usage*)

#define BASED_E_DETAIL_NUMARGS(...) (std::array {__VA_ARGS__}.size())
#define BASED_E_DETAIL_NUMARGS(...) based::u(std::array {__VA_ARGS__}.size())

#define BASED_E_DETAIL_LIST_ELEM_STR(Name, Index) #Name,

@@ -24,13 +24,14 @@

#define BASED_E_DETAIL_DECLARE_VAL(Name, Var, Index) static const Name Var;

#define BASED_E_DETAIL_DECLARE_CASE(Qualifier, Var, Index) \
case Qualifier::Var.value: \
case Qualifier::Var(): \
return Var;

#define BASED_E_DETAIL_DEFINE_VAL(Qualifier, Initial, Var, Index) \
inline constexpr BASED_E_DETAIL_SET( \
Qualifier::Var, \
Qualifier::value_type {Initial} + Qualifier::size - (Index) - 1 \
Qualifier::value_type {Initial} + Qualifier::size \
- Qualifier::value_type {Index} - Qualifier::value_type {1_u} \
)

#define BASED_E_DETAIL_DEFINE_NAMES(Qualifier, ...) \

@@ -42,7 +43,7 @@

inline const Qualifier& Qualifier::get(Type idx) \
{ \
/* NOLINTNEXTLINE(*paths-covered*) */ \
switch (idx) { \
switch (static_cast<Type::basic_type>(idx)) { \
BASED_FOREACH_1(Qualifier, BASED_E_DETAIL_DECLARE_CASE, __VA_ARGS__) \
default: \
break; \

@@ -57,10 +58,10 @@


#define BASED_DECLARE_ARRAY(Name, Initial) \
template<typename T> \
class array : public std::array<T, Name::size> \
class array : public based::array<T, Name::size_type, Name::size> \
{ \
using enum_type = Name; \
using base = std::array<T, enum_type::size>; \
using base = based::array<T, Name::size_type, enum_type::size>; \
using base::operator[]; \
using base::at; \
\

@@ -119,9 +120,9 @@

\
static const Name& get(Type idx); \
\
constexpr Type operator()() const \
constexpr auto operator()() const \
{ \
return value; \
return static_cast<Type::basic_type>(value); \
} \
\
friend bool operator==(Name lhs, Name rhs) \

diff --git a/ include/based/enum/enum_flag.hpp b/ include/based/enum/enum_flag.hpp

@@ -1,13 +1,15 @@

#pragma once

#include <cassert>
#include <array>

#include "based/assert.hpp"
#include "based/macro/foreach.hpp"
#include "based/macro/foreach_1.hpp"
#include "based/types/types.hpp"

// NOLINTBEGIN(*macro-usage*)

#define BASED_EF_DETAIL_NUMARGS(...) (std::array {__VA_ARGS__}.size())
#define BASED_EF_DETAIL_NUMARGS(...) based::u(std::array {__VA_ARGS__}.size())

#define BASED_EF_DETAIL_LIST_ELEM_STR(Name, Index) #Name,

@@ -22,25 +24,25 @@

static const Name Var;

#define BASED_EF_DETAIL_DECLARE_ENUM_CASE(Qualifier, Var, Index) \
case Qualifier::Var.value: \
case Qualifier::Var(): \
return Var;

#define BASED_EF_DETAIL_DEFINE_VAL(Qualifier, Var, Index) \
inline constexpr BASED_EF_DETAIL_SET( \
Qualifier::Var, \
Qualifier::value_type {1} \
<< Qualifier::value_type {Qualifier::size - (Index) - 2} \
#define BASED_EF_DETAIL_DEFINE_VAL(Qualifier, Var, Index) \
inline constexpr BASED_EF_DETAIL_SET( \
Qualifier::Var, \
Qualifier::value_type {1} << Qualifier:: \
value_type {Qualifier::size - Qualifier::value_type {Index} - Qualifier::value_type {2}} \
)

#define BASED_EF_DETAIL_DEFINE_VALS(Qualifier, First, ...) \
BASED_FOREACH_1(Qualifier, BASED_EF_DETAIL_DEFINE_VAL, __VA_ARGS__) \
inline constexpr BASED_EF_DETAIL_SET(Qualifier::First, 0)
inline constexpr BASED_EF_DETAIL_SET(Qualifier::First, based::u {0})

#define BASED_EF_DETAIL_DEFINE_GET(Qualifier, Type, ...) \
inline const Qualifier& Qualifier::get(Type idx) \
{ \
/* NOLINTNEXTLINE(*paths-covered*) */ \
switch (idx) { \
switch (static_cast<Type::basic_type>(idx)) { \
BASED_FOREACH_1( \
Qualifier, BASED_EF_DETAIL_DECLARE_ENUM_CASE, __VA_ARGS__ \
) \

@@ -146,6 +148,11 @@

return *this; \
} \
\
constexpr auto operator()() const \
{ \
return static_cast<Type::basic_type>(value); \
} \
\
Type value; \
\
BASED_FOREACH_1(Name, BASED_EF_DETAIL_DECLARE_ENUM_VAL, __VA_ARGS__) \

diff --git a/ test/source/enum_flag_test.cpp b/ test/source/enum_flag_test.cpp

@@ -10,10 +10,13 @@

#include "based/concepts/comparable/less.hpp"
#include "based/concepts/comparable/less_equal.hpp"
#include "based/concepts/is/same.hpp"
#include "based/types/literals.hpp"
#include "based/types/types.hpp"

BASED_DECLARE_ENUM_FLAG(var, based::bu8, empty, a, b, c)
BASED_DEFINE_ENUM_FLAG(var, based::bu8, empty, a, b, c)
using namespace based::literals; // NOLINT(*namespace*)

BASED_DECLARE_ENUM_FLAG(var, based::u8, empty, a, b, c)
BASED_DEFINE_ENUM_FLAG(var, based::u8, empty, a, b, c)

TEST_CASE("types", "[enum/enum_flag]")
{

@@ -22,11 +25,11 @@ TEST_CASE("types", "[enum/enum_flag]")

STATIC_REQUIRE(requires { var::a; });
STATIC_REQUIRE(requires { var::b; });
STATIC_REQUIRE(requires { var::c; });
STATIC_REQUIRE(var::size == 4);
STATIC_REQUIRE(var::empty.value == 0);
STATIC_REQUIRE(var::a.value == 1);
STATIC_REQUIRE(var::b.value == 2);
STATIC_REQUIRE(var::c.value == 4);
STATIC_REQUIRE(var::size == 4_u8);
STATIC_REQUIRE(var::empty.value == 0_u8);
STATIC_REQUIRE(var::a.value == 1_u8);
STATIC_REQUIRE(var::b.value == 2_u8);
STATIC_REQUIRE(var::c.value == 4_u8);
}

TEST_CASE("operations", "[enum/enum_flag]")

@@ -72,8 +75,7 @@ TEST_CASE("operations", "[enum/enum_flag]")


TEST_CASE("enum_flag_wrapper", "[enum/enum_flag_wrapper]")
{
based::bu8 flags = 0;

auto flags = 0_u8;
{
auto wrapper = based::enum_flag_wrapper<var>(flags);

@@ -81,5 +83,5 @@ TEST_CASE("enum_flag_wrapper", "[enum/enum_flag_wrapper]")

wrapper |= var::c;
}

REQUIRE(flags == 0x5);
REQUIRE(flags == 0x5_u8);
}

diff --git a/ test/source/enum_test.cpp b/ test/source/enum_test.cpp

@@ -11,11 +11,14 @@

#include "based/concepts/comparable/less_equal.hpp"
#include "based/concepts/is/invocable.hpp"
#include "based/concepts/is/same.hpp"
#include "based/types/literals.hpp"
#include "based/types/types.hpp"

using namespace based::literals; // NOLINT(*namespace*)

struct test
{
BASED_DECLARE_ENUM(var, based::bu8, 0, a, b, c)
BASED_DECLARE_ENUM(var, based::u8, 0_u8, a, b, c)

[[nodiscard]] int get_var(var req) const;

@@ -25,7 +28,7 @@ private:

int m_c = 3;
};

BASED_DEFINE_ENUM_CLASS(test, var, based::bu8, 0, a, b, c)
BASED_DEFINE_ENUM_CLASS(test, var, based::u8, 0_u8, a, b, c)

inline int test::get_var(var req) const
{

@@ -47,7 +50,7 @@ TEST_CASE("types", "[enum/enum]")

STATIC_REQUIRE(requires { test::var::a; });
STATIC_REQUIRE(requires { test::var::b; });
STATIC_REQUIRE(requires { test::var::c; });
STATIC_REQUIRE(test::var::size == 3);
STATIC_REQUIRE(test::var::size == 3_u8);
STATIC_REQUIRE(test::var::a() == 0);
STATIC_REQUIRE(test::var::b() == 1);
STATIC_REQUIRE(test::var::c() == 2);

@@ -61,7 +64,7 @@ TEST_CASE("safety", "[enum/enum]")

REQUIRE(crnt.get_var(test::var::b) == 2);
REQUIRE(crnt.get_var(test::var::c) == 3);

REQUIRE(!based::Invocable<decltype(&test::get_var), based::bu8>);
REQUIRE(!based::Invocable<decltype(&test::get_var), based::u8>);
REQUIRE(!based::Invocable<decltype(&test::get_var), int>);
}