basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 3506c973a5e2cff3e6494c34e7feb376990bd72e |
parent | 042d328c9c2924b0c1490bbe45e3c5695b761f7b |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Fri, 9 May 2025 14:37:15 +0200 |
Add strong_types
A | include/based/types/strong.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | test/CMakeLists.txt | | | ++++ - |
A | test/source/types/strong_type_test.cpp | | | ++++++++++++++++++++++++++++++++++++ |
3 files changed, 301 insertions(+), 1 deletions(-)
diff --git a/ include/based/types/strong.hpp b/ include/based/types/strong.hpp
@@ -0,0 +1,261 @@
#pragma once
#include "based/concepts/is/same.hpp"
namespace based
{
// NOLINTNEXTLINE(*macro*)
#define BASED_DETAIL_MACRO(decl, val) \
decltype(decl) \
{ \
static_cast<decltype(decl)::value_type>(val) \
}
template<class ValueType, class Tag>
// NOLINTNEXTLINE(*crtp*)
struct strong_type
{
using value_type = ValueType;
using tag_type = Tag;
ValueType value;
};
template<class LHS, class RHS>
concept addable = requires(LHS lhs, RHS rhs) { add(lhs, rhs); };
template<class LHS, class RHS>
requires addable<LHS, RHS>
constexpr auto operator+(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(add(lhs, rhs), lhs.value + rhs.value);
}
template<class LHS, class RHS>
concept subtractable = requires(LHS lhs, RHS rhs) { sub(lhs, rhs); };
template<class LHS, class RHS>
requires subtractable<LHS, RHS>
constexpr auto operator-(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(sub(lhs, rhs), lhs.value - rhs.value);
}
template<class LHS, class RHS>
concept multiplyable = requires(LHS lhs, RHS rhs) { mul(lhs, rhs); };
template<class LHS, class RHS>
requires multiplyable<LHS, RHS>
constexpr auto operator*(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(mul(lhs, rhs), lhs.value * rhs.value);
}
template<class LHS, class RHS>
concept divisible = requires(LHS lhs, RHS rhs) { div(lhs, rhs); };
template<class LHS, class RHS>
requires divisible<LHS, RHS>
constexpr auto operator/(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(div(lhs, rhs), lhs.value / rhs.value);
}
template<class LHS, class RHS>
concept modable = requires(LHS lhs, RHS rhs) { mod(lhs, rhs); };
template<class LHS, class RHS>
requires modable<LHS, RHS>
constexpr auto operator%(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(mod(lhs, rhs), lhs.value % rhs.value);
}
template<class LHS>
concept preincable = requires(LHS lhs) { preinc(lhs); };
template<class LHS>
requires preincable<LHS>
constexpr auto& operator++(LHS& lhs)
{
++lhs.value;
return lhs;
}
template<class LHS>
concept postincable = requires(LHS lhs) { postinc(lhs); };
template<class LHS>
requires postincable<LHS>
constexpr auto operator++(LHS& lhs, int)
{
return BASED_DETAIL_MACRO(postinc(lhs), lhs.value++);
}
template<class LHS>
concept predecable = requires(LHS lhs) { predec(lhs); };
template<class LHS>
requires predecable<LHS>
constexpr auto operator--(LHS& lhs)
{
--lhs.value;
return lhs;
}
template<class LHS>
concept postdecable = requires(LHS lhs) { postdec(lhs); };
template<class LHS>
requires postdecable<LHS>
constexpr auto operator--(LHS lhs, int)
{
return BASED_DETAIL_MACRO(postdec(lhs), lhs.value--);
}
template<class LHS>
concept neggable = requires(LHS lhs) { lneg(lhs); };
template<class LHS>
requires neggable<LHS>
constexpr auto operator~(LHS lhs)
{
return BASED_DETAIL_MACRO(lneg(lhs), ~lhs.value);
}
template<class LHS, class RHS>
concept andable = requires(LHS lhs, RHS rhs) { land(lhs, rhs); };
template<class LHS, class RHS>
requires andable<LHS, RHS>
constexpr auto operator&(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(land(lhs, rhs), lhs.value & rhs.value);
}
template<class LHS, class RHS>
concept orable = requires(LHS lhs, RHS rhs) { lor(lhs, rhs); };
template<class LHS, class RHS>
requires orable<LHS, RHS>
constexpr auto operator|(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(lor(lhs, rhs), lhs.value | rhs.value);
}
template<class LHS, class RHS>
concept xorable = requires(LHS lhs, RHS rhs) { lxor(lhs, rhs); };
template<class LHS, class RHS>
requires xorable<LHS, RHS>
constexpr auto operator^(LHS lhs, RHS rhs)
{
return BASED_DETAIL_MACRO(lxor(lhs, rhs), lhs.value ^ rhs.value);
}
template<class LHS, class RHS = LHS>
concept equal_addable = requires(LHS lhs, RHS rhs) { eadd(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_addable<LHS, RHS>
constexpr auto& operator+=(LHS& lhs, RHS rhs)
{
lhs.value += rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_subtractable = requires(LHS lhs, RHS rhs) { esub(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_subtractable<LHS, RHS>
constexpr auto& operator-=(LHS& lhs, RHS rhs)
{
lhs.value -= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_multiplyable = requires(LHS lhs, RHS rhs) { emul(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_multiplyable<LHS, RHS>
constexpr auto& operator*=(LHS& lhs, RHS rhs)
{
lhs.value *= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_divisible = requires(LHS lhs, RHS rhs) { ediv(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_divisible<LHS, RHS>
constexpr auto& operator/=(LHS& lhs, RHS rhs)
{
lhs.value /= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_modable = requires(LHS lhs, RHS rhs) { emod(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_modable<LHS, RHS>
constexpr auto& operator%=(LHS& lhs, RHS rhs)
{
lhs.value %= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_andable = requires(LHS lhs, RHS rhs) { eland(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_andable<LHS, RHS>
constexpr auto& operator&=(LHS& lhs, RHS rhs)
{
lhs.value &= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_orable = requires(LHS lhs, RHS rhs) { elor(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_orable<LHS, RHS>
constexpr auto& operator|=(LHS& lhs, RHS rhs)
{
lhs.value |= rhs.value;
return lhs;
}
template<class LHS, class RHS = LHS>
concept equal_xorable = requires(LHS lhs, RHS rhs) { elxor(lhs, rhs); };
template<class LHS, class RHS = LHS>
requires equal_xorable<LHS, RHS>
constexpr auto& operator^=(LHS& lhs, RHS rhs)
{
lhs.value ^= rhs.value;
return lhs;
}
template<class LHS, class RHS>
concept equalable = requires(LHS lhs, RHS rhs) {
{
equal(lhs, rhs)
} -> SameAs<bool>;
};
template<class LHS, class RHS>
requires equalable<LHS, RHS>
constexpr bool operator==(LHS lhs, RHS rhs)
{
return lhs.value == rhs.value;
}
#undef BASED_DETAIL_MACRO
} // namespace based
diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -25,8 +25,11 @@
function(add_test DIR NAME)
catch_discover_tests("${NAME}")
endfunction()
## ----- Types -----
## ----- Type Traits -----
add_test(types strong_type_test)
## ----- Trait -----
add_test(trait invoke_result_test)
diff --git a/ test/source/types/strong_type_test.cpp b/ test/source/types/strong_type_test.cpp
@@ -0,0 +1,36 @@
// #define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include "based/types/strong.hpp"
#include <catch2/catch_test_macros.hpp>
#include "based/types/types.hpp"
struct t1 : based::strong_type<based::u8, t1>
{
};
struct t2 : based::strong_type<based::u8, t2>
{
};
// NOLINTBEGIN(*needed*,*internal-linkage*)
auto equal(t1, t1) -> bool;
auto equal(t2, t2) -> bool;
auto add(t1, t1) -> t1;
auto add(t1, t2) -> t1;
auto add(t2, t1) -> t2;
// NOLINTEND(*needed*,*internal-linkage*)
TEST_CASE("strong_type", "[types/strong_type]")
{
STATIC_REQUIRE(based::addable<t1, t1>);
STATIC_REQUIRE(!based::addable<t2, t2>);
STATIC_REQUIRE(based::addable<t1, t2>);
STATIC_REQUIRE(based::addable<t2, t1>);
REQUIRE(t1 {10} + t1 {20} == t1 {30});
REQUIRE(t1 {10} + t2 {20} == t1 {30});
REQUIRE(t2 {10} + t1 {20} == t2 {30});
}