based

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

commit f92e7681dde7fca63aa0ef1fb992253afa2e94a1
parent 621a3e2bf69c4d2f5f97c72aec57ded3d2f9d3b6
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Sun, 4 May 2025 22:31:31 +0200

Enum indexed template array, cleaner code

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

5 files changed, 92 insertions(+), 47 deletions(-)


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

@@ -1,19 +1,72 @@

#pragma once

#include <array>
#include <cassert>

#include "based/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__)

#define BASED_DECLARE_ENUM_VAL(Name, Index) static const type Name;

#define BASED_DEFINE_ENUM_VAL(Macro, Name, Index) \
inline constexpr Macro::type Macro::Name = {Macro::type::size - (Index) - 1};
#define BASED_DECLARE_ENUM_CASE(Qualifier, Name, Index) \
case Qualifier::Name.value: \
return Name;

#define BASED_DEFINE_ENUM_VAL(Qualifier, Name, Index) \
inline constexpr Qualifier::type Qualifier::Name = { \
Qualifier::type::size - (Index) - 1 \
};

#define BASED_DEFINE_ENUM_VALS(Qualifier, ...) \
BASED_FOREACH_1(Qualifier, BASED_DEFINE_ENUM_VAL, __VA_ARGS__)

#define BASED_DEFINE_ENUM_CLASS_VAL(Class, Macro, Name, Index) \
inline constexpr Class::Macro::type Class::Macro::Name = { \
Class::Macro::type::size - (Index) - 1 \
#define BASED_DEFINE_ENUM_NAMES(Qualifier, ...) \
inline constexpr Qualifier::type::array<const char*> \
Qualifier::type::names = {BASED_LIST_STR(__VA_ARGS__)};

#define BASED_DEFINE_ENUM_GET(Qualifier, Type, ...) \
inline const Qualifier::type& Qualifier::type::get(Type idx) \
{ \
switch (idx) { \
BASED_FOREACH_1(Qualifier, BASED_DECLARE_ENUM_CASE, __VA_ARGS__) \
default: \
break; \
} \
assert(0); /* NOLINT(*assert*,cert-dcl03-c) */ \
}

#define BASED_DEFINE_ENUM_ARRAY(Name) \
template<typename T> \
class array : public std::array<T, Name::type::size> \
{ \
using std::array<T, Name::type::size>::operator[]; \
\
public: \
constexpr array() = default; \
\
/* NOLINTBEGIN(*explicit*,*decay*)*/ \
template<class... Args> \
requires(sizeof...(Args) == Name::type::size) \
constexpr array(Args&&... args) \
: std::array<T, Name::type::size>({std::forward<Args>(args)...}) \
{ \
} \
/* NOLINTEND(*explicit*,*decay*)*/ \
\
const T& operator[](Name::type val) const \
{ \
return std::array<T, Name::type::size>::operator[](val.value); \
} \
\
T& operator[](Name::type val) \
{ \
return std::array<T, Name::type::size>::operator[](val.value); \
} \
};

#define BASED_DECLARE_ENUM(Name, Type, ...) \

@@ -29,18 +82,15 @@

{ \
} \
\
static constexpr std::array m_names = { \
BASED_FOREACH(BASED_INIT_LIST_STRING, __VA_ARGS__) \
}; \
\
public: \
static constexpr auto size = m_names.size(); \
static constexpr auto names = m_names; \
using size_t = Type; \
static constexpr size_t size = \
BASED_NUMARGS(BASED_LIST_STR(__VA_ARGS__)); \
\
static auto name(type value) \
{ \
return m_names[value.value]; \
} \
BASED_DEFINE_ENUM_ARRAY(Name) \
static const array<const char*> names; \
\
static const type& get(Type idx); \
\
Type value; \
}; \

@@ -48,10 +98,14 @@

BASED_FOREACH(BASED_DECLARE_ENUM_VAL, __VA_ARGS__) \
};

#define BASED_DEFINE_ENUM(Class, Name, Type, ...) \
BASED_FOREACH_1(Name, BASED_DEFINE_ENUM_VAL, __VA_ARGS__)
#define BASED_DEFINE_ENUM(Name, Type, ...) \
BASED_DEFINE_ENUM_VALS(Name, __VA_ARGS__) \
BASED_DEFINE_ENUM_NAMES(Name, __VA_ARGS__) \
BASED_DEFINE_ENUM_GET(Name, __VA_ARGS__)

#define BASED_DEFINE_CLASS_ENUM(Class, Name, Type, ...) \
BASED_FOREACH_2(Class, Name, BASED_DEFINE_ENUM_CLASS_VAL, __VA_ARGS__)
BASED_DEFINE_ENUM_VALS(Class::Name, __VA_ARGS__) \
BASED_DEFINE_ENUM_NAMES(Class::Name, __VA_ARGS__) \
BASED_DEFINE_ENUM_GET(Class::Name, Type, __VA_ARGS__)

// NOLINTEND(*macro-usage*)

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

@@ -89,17 +89,13 @@ struct instrumented_base

comparison
)

static auto& count(op::type opr) { return counts[opr.value]; }
static auto& count(std::size_t idx) { return counts[idx]; }
static op::type::array<double> counts;

static void initialize(std::size_t size)
{
std::fill(std::begin(counts), std::end(counts), 0.0);
count(op::n) = static_cast<double>(size);
counts[op::n] = static_cast<double>(size);
}

private:
static std::array<double, op::type::size> counts;
};

BASED_DEFINE_CLASS_ENUM(

@@ -129,42 +125,42 @@ struct instrumented : instrumented_base

instrumented(const value_type& val) // NOLINT(*explicit*)
: value(std::move(val))
{
++count(op::ctor_value);
++counts[op::ctor_value];
;
}

instrumented(value_type&& val) // NOLINT(*explicit*)
: value(std::move(val))
{
++count(op::ctor_value);
++counts[op::ctor_value];
;
}

// Semiregular:
instrumented()
{
++count(op::ctor_default);
++counts[op::ctor_default];
;
}

instrumented(const instrumented& val)
: value(val.value)
{
++count(op::ctor_copy);
++counts[op::ctor_copy];
;
}

instrumented(instrumented&& val) noexcept
: value(std::move(val.value))
{
++count(op::ctor_move);
++counts[op::ctor_move];
;
}

// self assign should be handled by the value_type
instrumented& operator=(const instrumented& val) // NOLINT(*cert-oop54-cpp*)
{
++count(op::asgn_copy);
++counts[op::asgn_copy];
;
value = val.value;
return *this;

@@ -174,7 +170,7 @@ struct instrumented : instrumented_base

instrumented& operator=(instrumented&& val
) noexcept // NOLINT(*cert-oop54-cpp*)
{
++count(op::asgn_move);
++counts[op::asgn_move];
;
value = std::move(val.value);
return *this;

@@ -182,7 +178,7 @@ struct instrumented : instrumented_base


~instrumented()
{
++count(op::destructor);
++counts[op::destructor];
;
}

@@ -190,7 +186,7 @@ struct instrumented : instrumented_base


friend bool operator==(const instrumented& lhs, const instrumented& rhs)
{
++count(op::equality);
++counts[op::equality];
;
return lhs.value == rhs.value;
}

@@ -204,7 +200,7 @@ struct instrumented : instrumented_base


friend bool operator<(const instrumented& lhs, const instrumented& rhs)
{
++count(op::comparison);
++counts[op::comparison];
;
return lhs.value < rhs.value;
}

@@ -293,6 +289,7 @@ void count_operations(

)
{
using instrumented = based::instrumented<double>;
using size_t = instrumented::op::type::size_t;

constexpr size_t cols = instrumented::op::type::size;
const size_t decimals((norm == dont_normalize) ? 0 : 2);

@@ -319,7 +316,8 @@ void count_operations(


values[0] = dbl;
for (size_t k = 1; k < cols; ++k) {
values[k] = norm(instrumented::count(k), dbl);
const auto& val = instrumented::op::type::get(k);
values[k] = norm(instrumented::counts[val], dbl);
}

tbl.print_row(std::begin(values), std::end(values), decimals);

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

@@ -2,13 +2,7 @@


// NOLINTBEGIN(*macro-usage*)

#define BASED_NAME(Name, Index) NAME
#define BASED_INDEX(Name, Index) INDEX

#define BASED_SIZE(Name, Index) INDEX

#define BASED_INIT_LIST(Name, Index) Name,
#define BASED_INIT_LIST_STRING(Name, Index) #Name,
#define BASED_NUMARGS(...) (std::array{__VA_ARGS__}.size())

// clang-format off
#define BASED_GET_MACRO( \

diff --git a/ source/instrumentation.cpp b/ source/instrumentation.cpp

@@ -3,7 +3,6 @@

namespace based
{

std::array<double, instrumented_base::op::type::size>
instrumented_base::counts = {};
instrumented_base::op::type::array<double> instrumented_base::counts = {};

} // namespace based

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

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


TEST_CASE("names", "[enum/enum]")
{
REQUIRE(std::strcmp(test::var::type::name(test::var::a), "a") == 0);
REQUIRE(std::strcmp(test::var::type::name(test::var::b), "b") == 0);
REQUIRE(std::strcmp(test::var::type::name(test::var::c), "c") == 0);
REQUIRE(std::strcmp(test::var::type::names[test::var::a], "a") == 0);
REQUIRE(std::strcmp(test::var::type::names[test::var::b], "b") == 0);
REQUIRE(std::strcmp(test::var::type::names[test::var::c], "c") == 0);
}