basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 621a3e2bf69c4d2f5f97c72aec57ded3d2f9d3b6 |
parent | e23e7e98862df4b47ff361e3151c685104e45ca1 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Sun, 4 May 2025 19:27:05 +0200 |
Proper enum-like system, dabbling with macros now
M | .clang-format | | | ++ - |
M | CMakeLists.txt | | | + - |
M | include/based/enum.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++ ----------------------------------- |
M | include/based/instrumentation.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++ ----------------------------- |
A | include/based/macros.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | source/instrumentation.cpp | | | ++ ---------- |
M | test/CMakeLists.txt | | | ++++ |
M | test/source/callable_test.cpp | | | - |
A | test/source/enum_test.cpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | test/source/max_test.cpp | | | + - |
M | test/source/scopeguard_test.cpp | | | + - |
11 files changed, 270 insertions(+), 84 deletions(-)
diff --git a/ .clang-format b/ .clang-format
@@ -7,7 +7,7 @@
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveBitFields: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: DontAlign
AlignEscapedNewlines: Right
AlignOperands: DontAlign
AlignTrailingComments: false
AllowAllArgumentsOnNextLine: true
@@ -70,6 +70,7 @@
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
- BASED_FOREACH
IncludeBlocks: Regroup
IncludeCategories:
# Standard library headers come before anything else
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -4,7 +4,7 @@
include(cmake/prelude.cmake)
project(
based
VERSION 0.1.1
VERSION 0.1.2
DESCRIPTION "Opinionated utility library"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/based.git"
LANGUAGES CXX
diff --git a/ include/based/enum.hpp b/ include/based/enum.hpp
@@ -1,43 +1,57 @@
#pragma once
#include <array> // NOLINT
#include <cstddef> // NOLINT
namespace based
{
#define BASED_ENUM(Name, Type, ...) \
class Name \
{ \
public: \
enum Name##_ : Type {__VA_ARGS__}; \
\
private: \
static constexpr const std::array arr = {__VA_ARGS__}; \
\
public: \
static constexpr const std::size_t size = arr.size(); \
\
using enum_type = Name##_; \
\
explicit Name(Name::Name##_ val) \
: m_value(val) \
{ \
} \
\
operator enum_type() const \
{ \
return m_value; \
} \
\
Name& operator=(enum_type val) \
{ \
m_value = val; \
return *this; \
} \
\
private: \
enum_type m_value; \
#include <array>
#include "based/macros.hpp"
// NOLINTBEGIN(*macro-usage*)
#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_DEFINE_ENUM_CLASS_VAL(Class, Macro, Name, Index) \
inline constexpr Class::Macro::type Class::Macro::Name = { \
Class::Macro::type::size - (Index) - 1 \
};
} // namespace based
#define BASED_DECLARE_ENUM(Name, Type, ...) \
struct Name \
{ \
class type \
{ \
friend Name; \
\
/* NOLINTNEXTLINE(*explicit*) */ \
constexpr type(Type enum_value) \
: value(enum_value) \
{ \
} \
\
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; \
\
static auto name(type value) \
{ \
return m_names[value.value]; \
} \
\
Type value; \
}; \
\
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_CLASS_ENUM(Class, Name, Type, ...) \
BASED_FOREACH_2(Class, Name, BASED_DEFINE_ENUM_CLASS_VAL, __VA_ARGS__)
// NOLINTEND(*macro-usage*)
diff --git a/ include/based/instrumentation.hpp b/ include/based/instrumentation.hpp
@@ -74,7 +74,7 @@
inline auto normalize_nlogn1(double x, double n)
struct instrumented_base
{
BASED_ENUM(
BASED_DECLARE_ENUM(
op,
std::uint8_t,
n,
@@ -89,26 +89,35 @@
struct instrumented_base
comparison
)
static constexpr std::array<const char*, op::size> names = {
"n",
"ctor_default",
"ctor_value",
"ctor_copy",
"ctor_move",
"asgn_copy",
"asgn_move",
"destructor",
"equality",
"comparison",
};
static auto& count(op::type opr) { return counts[opr.value]; }
static auto& count(std::size_t idx) { return counts[idx]; }
static std::array<double, op::size> counts;
static constexpr auto op_num = op::size;
static void initialize(std::size_t size)
{
std::fill(std::begin(counts), std::end(counts), 0.0);
count(op::n) = static_cast<double>(size);
}
static void initialize(std::size_t size);
private:
static std::array<double, op::type::size> counts;
};
BASED_DEFINE_CLASS_ENUM(
instrumented_base,
op,
std::uint8_t,
n,
ctor_default,
ctor_value,
ctor_copy,
ctor_move,
asgn_copy,
asgn_move,
destructor,
equality,
comparison
)
template<typename T>
requires std::semiregular<T>
struct instrumented : instrumented_base
@@ -120,34 +129,43 @@
struct instrumented : instrumented_base
instrumented(const value_type& val) // NOLINT(*explicit*)
: value(std::move(val))
{
++counts[op::ctor_value];
++count(op::ctor_value);
;
}
instrumented(value_type&& val) // NOLINT(*explicit*)
: value(std::move(val))
{
++counts[op::ctor_value];
++count(op::ctor_value);
;
}
// Semiregular:
instrumented() { ++counts[op::ctor_default]; }
instrumented()
{
++count(op::ctor_default);
;
}
instrumented(const instrumented& val)
: value(val.value)
{
++counts[op::ctor_copy];
++count(op::ctor_copy);
;
}
instrumented(instrumented&& val) noexcept
: value(std::move(val.value))
{
++counts[op::ctor_move];
++count(op::ctor_move);
;
}
// self assign should be handled by the value_type
instrumented& operator=(const instrumented& val) // NOLINT(*cert-oop54-cpp*)
{
++counts[op::asgn_copy];
++count(op::asgn_copy);
;
value = val.value;
return *this;
}
@@ -156,18 +174,24 @@
struct instrumented : instrumented_base
instrumented& operator=(instrumented&& val
) noexcept // NOLINT(*cert-oop54-cpp*)
{
++counts[op::asgn_move];
++count(op::asgn_move);
;
value = std::move(val.value);
return *this;
}
~instrumented() { ++counts[op::destructor]; }
~instrumented()
{
++count(op::destructor);
;
}
// Regular
friend bool operator==(const instrumented& lhs, const instrumented& rhs)
{
++counts[op::equality];
++count(op::equality);
;
return lhs.value == rhs.value;
}
@@ -180,7 +204,8 @@
struct instrumented : instrumented_base
friend bool operator<(const instrumented& lhs, const instrumented& rhs)
{
++counts[op::comparison];
++count(op::comparison);
;
return lhs.value < rhs.value;
}
@@ -269,7 +294,7 @@
void count_operations(
{
using instrumented = based::instrumented<double>;
constexpr size_t cols = instrumented::op_num;
constexpr size_t cols = instrumented::op::type::size;
const size_t decimals((norm == dont_normalize) ? 0 : 2);
std::array<double, cols> values = {0};
@@ -277,7 +302,8 @@
void count_operations(
static constexpr int width = 12;
table tbl(width);
tbl.print_header(
std::begin(instrumented::names), std::end(instrumented::names)
std::begin(instrumented::op::type::names),
std::end(instrumented::op::type::names)
);
std::mt19937 rng(0); // NOLINT(*cert-msc32-c*, *cert-msc51-cpp*)
@@ -293,7 +319,7 @@
void count_operations(
values[0] = dbl;
for (size_t k = 1; k < cols; ++k) {
values[k] = norm(instrumented::counts[k], dbl);
values[k] = norm(instrumented::count(k), dbl);
}
tbl.print_row(std::begin(values), std::end(values), decimals);
diff --git a/ include/based/macros.hpp b/ include/based/macros.hpp
@@ -0,0 +1,93 @@
#pragma once
// 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,
// clang-format off
#define BASED_GET_MACRO( \
_0, _1, _2, _3, _4, _5, _6, _7, \
_8, _9, _10, _11, _12, _13, _14, _15, \
NAME, ... \
) NAME
// clang-format on
// clang-format off
#define BASED_FE_0(WHAT)
#define BASED_FE_1(WHAT, X) WHAT(X, 0)
#define BASED_FE_2(WHAT, X, ...) WHAT(X, 1) BASED_FE_1(WHAT, __VA_ARGS__)
#define BASED_FE_3(WHAT, X, ...) WHAT(X, 2) BASED_FE_2(WHAT, __VA_ARGS__)
#define BASED_FE_4(WHAT, X, ...) WHAT(X, 3) BASED_FE_3(WHAT, __VA_ARGS__)
#define BASED_FE_5(WHAT, X, ...) WHAT(X, 4) BASED_FE_4(WHAT, __VA_ARGS__)
#define BASED_FE_6(WHAT, X, ...) WHAT(X, 5) BASED_FE_5(WHAT, __VA_ARGS__)
#define BASED_FE_7(WHAT, X, ...) WHAT(X, 6) BASED_FE_6(WHAT, __VA_ARGS__)
#define BASED_FE_8(WHAT, X, ...) WHAT(X, 7) BASED_FE_7(WHAT, __VA_ARGS__)
#define BASED_FE_9(WHAT, X, ...) WHAT(X, 8) BASED_FE_8(WHAT, __VA_ARGS__)
#define BASED_FE_10(WHAT, X, ...) WHAT(X, 9) BASED_FE_9(WHAT, __VA_ARGS__)
#define BASED_FE_11(WHAT, X, ...) WHAT(X, 10) BASED_FE_10(WHAT, __VA_ARGS__)
#define BASED_FE_12(WHAT, X, ...) WHAT(X, 11) BASED_FE_11(WHAT, __VA_ARGS__)
#define BASED_FE_13(WHAT, X, ...) WHAT(X, 12) BASED_FE_12(WHAT, __VA_ARGS__)
#define BASED_FE_14(WHAT, X, ...) WHAT(X, 13) BASED_FE_13(WHAT, __VA_ARGS__)
#define BASED_FE_15(WHAT, X, ...) WHAT(X, 14) BASED_FE_14(WHAT, __VA_ARGS__)
// clang-format on
#define BASED_FOREACH(action, ...) \
BASED_GET_MACRO(_0, __VA_ARGS__, BASED_FE_15, BASED_FE_14, BASED_FE_13, BASED_FE_12, BASED_FE_11, BASED_FE_10, BASED_FE_9, BASED_FE_8, BASED_FE_7, BASED_FE_6, BASED_FE_5, BASED_FE_4, BASED_FE_3, BASED_FE_2, BASED_FE_1, BASED_FE_0)( \
action, __VA_ARGS__ \
)
// clang-format off
#define BASED_FE_0_1(First, WHAT)
#define BASED_FE_1_1(First, WHAT, X) WHAT(First, X, 0)
#define BASED_FE_2_1(First, WHAT, X, ...) WHAT(First, X, 1) BASED_FE_1_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_3_1(First, WHAT, X, ...) WHAT(First, X, 2) BASED_FE_2_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_4_1(First, WHAT, X, ...) WHAT(First, X, 3) BASED_FE_3_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_5_1(First, WHAT, X, ...) WHAT(First, X, 4) BASED_FE_4_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_6_1(First, WHAT, X, ...) WHAT(First, X, 5) BASED_FE_5_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_7_1(First, WHAT, X, ...) WHAT(First, X, 6) BASED_FE_6_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_8_1(First, WHAT, X, ...) WHAT(First, X, 7) BASED_FE_7_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_9_1(First, WHAT, X, ...) WHAT(First, X, 8) BASED_FE_8_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_10_1(First, WHAT, X, ...) WHAT(First, X, 9) BASED_FE_9_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_11_1(First, WHAT, X, ...) WHAT(First, X, 10) BASED_FE_10_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_12_1(First, WHAT, X, ...) WHAT(First, X, 11) BASED_FE_11_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_13_1(First, WHAT, X, ...) WHAT(First, X, 12) BASED_FE_12_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_14_1(First, WHAT, X, ...) WHAT(First, X, 13) BASED_FE_13_1(First, WHAT, __VA_ARGS__)
#define BASED_FE_15_1(First, WHAT, X, ...) WHAT(First, X, 14) BASED_FE_14_1(First, WHAT, __VA_ARGS__)
// clang-format on
#define BASED_FOREACH_1(First, action, ...) \
BASED_GET_MACRO(_0, __VA_ARGS__, BASED_FE_15_1, BASED_FE_14_1, BASED_FE_13_1, BASED_FE_12_1, BASED_FE_11_1, BASED_FE_10_1, BASED_FE_9_1, BASED_FE_8_1, BASED_FE_7_1, BASED_FE_6_1, BASED_FE_5_1, BASED_FE_4_1, BASED_FE_3_1, BASED_FE_2_1, BASED_FE_1_1, BASED_FE_0_1)( \
First, action, __VA_ARGS__ \
)
// clang-format off
#define BASED_FE_0_2(First, Second, WHAT)
#define BASED_FE_1_2(First, Second, WHAT, X) WHAT(First, Second, X, 0)
#define BASED_FE_2_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 1) BASED_FE_1_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_3_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 2) BASED_FE_2_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_4_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 3) BASED_FE_3_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_5_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 4) BASED_FE_4_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_6_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 5) BASED_FE_5_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_7_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 6) BASED_FE_6_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_8_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 7) BASED_FE_7_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_9_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 8) BASED_FE_8_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_10_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 9) BASED_FE_9_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_11_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 10) BASED_FE_10_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_12_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 11) BASED_FE_11_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_13_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 12) BASED_FE_12_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_14_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 13) BASED_FE_13_2(First, Second, WHAT, __VA_ARGS__)
#define BASED_FE_15_2(First, Second, WHAT, X, ...) WHAT(First, Second, X, 14) BASED_FE_14_2(First, Second, WHAT, __VA_ARGS__)
// clang-format on
#define BASED_FOREACH_2(First, Second, action, ...) \
BASED_GET_MACRO(_0, __VA_ARGS__, BASED_FE_15_2, BASED_FE_14_2, BASED_FE_13_2, BASED_FE_12_2, BASED_FE_11_2, BASED_FE_10_2, BASED_FE_9_2, BASED_FE_8_2, BASED_FE_7_2, BASED_FE_6_2, BASED_FE_5_2, BASED_FE_4_2, BASED_FE_3_2, BASED_FE_2_2, BASED_FE_1_2, BASED_FE_0_2)( \
First, Second, action, __VA_ARGS__ \
)
// NOLINTEND(*macro-usage*)
diff --git a/ source/instrumentation.cpp b/ source/instrumentation.cpp
@@ -1,17 +1,9 @@
#include <algorithm>
#include <iterator>
#include "based/instrumentation.hpp"
namespace based
{
std::array<double, instrumented_base::op_num> instrumented_base::counts = {};
void instrumented_base::initialize(std::size_t size)
{
std::fill(std::begin(counts), std::end(counts), 0.0);
counts[op::n] = static_cast<double>(size);
}
std::array<double, instrumented_base::op::type::size>
instrumented_base::counts = {};
} // namespace based
diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -79,6 +79,10 @@
add_test(scopeguard_test)
add_test(curry_test)
# ------ Enum -----
add_test(enum_test)
# ---- End-of-file commands ----
add_folders(Test)
diff --git a/ test/source/callable_test.cpp b/ test/source/callable_test.cpp
@@ -4,7 +4,6 @@
#include "based/type_traits.hpp"
namespace
{
diff --git a/ test/source/enum_test.cpp b/ test/source/enum_test.cpp
@@ -0,0 +1,57 @@
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include "based/enum.hpp"
#include <catch2/catch_test_macros.hpp>
struct test
{
BASED_DECLARE_ENUM(var, std::uint8_t, a, b, c)
[[nodiscard]] int get_var(var::type req) const;
private:
int m_a = 1;
int m_b = 2;
int m_c = 3;
};
BASED_DEFINE_CLASS_ENUM(test, var, std::uint8_t, a, b, c)
inline int test::get_var(var::type req) const
{
switch (req.value) {
case var::a.value:
return m_a;
case var::b.value:
return m_b;
case var::c.value:
return m_c;
default:
return -1;
}
}
TEST_CASE("types", "[enum/enum]")
{
STATIC_REQUIRE(requires { typename test::var; });
STATIC_REQUIRE(requires { test::var::a; });
STATIC_REQUIRE(requires { test::var::b; });
STATIC_REQUIRE(requires { test::var::c; });
}
TEST_CASE("safety", "[enum/enum]")
{
const test crnt;
STATIC_REQUIRE(requires { crnt.get_var(test::var::a) == 1; });
STATIC_REQUIRE(requires { crnt.get_var(test::var::b) == 2; });
STATIC_REQUIRE(requires { crnt.get_var(test::var::c) == 3; });
}
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);
}
diff --git a/ test/source/max_test.cpp b/ test/source/max_test.cpp
@@ -1,7 +1,7 @@
#include <catch2/catch_test_macros.hpp>
#include "based/type_traits.hpp"
#include "based/algorithm.hpp"
#include "based/type_traits.hpp"
// NOLINTBEGIN(*const-arg*,*const-correctness*,*after-move, *access-moved)
diff --git a/ test/source/scopeguard_test.cpp b/ test/source/scopeguard_test.cpp
@@ -42,7 +42,7 @@
TEST_CASE("manual", "[template/scopeguard]")
{
int test = 0;
try {
based::scopeguard guard = set(test); // NOLINT(*const*)
based::scopeguard guard = set(test); // NOLINT(*const*)
} catch (...) {
test *= 1;
}