basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 2f580cb189783d568e4822af13c7accce7a92ba5 |
parent | d488fb8ef4714a3370fb5aa6788adc9f2069daf1 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Mon, 2 Jun 2025 17:03:59 +0200 |
Rework literals, add limits
A | include/based/concepts/is/castable.hpp | | | +++++++++++ |
A | include/based/types/limits.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | include/based/types/literals.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | include/based/types/strong.hpp | | | ++++++++++++++++++++++++ - |
M | include/based/types/types.hpp | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ------------------------ |
M | test/CMakeLists.txt | | | ++ |
M | test/source/list_test.cpp | | | ++++++++++++++++++++++++++++++++++ --------------------------- |
A | test/source/types/limits.cpp | | | +++++++++++++++++++++++++++++++++ |
A | test/source/types/literals.cpp | | | +++++++++++++++++++++++++++++++++++++++++ |
M | test/source/types/strong_type_test.cpp | | | ++ -- |
M | test/source/types/type_test.cpp | | | + - |
M | test/source/utility/buffer_test.cpp | | | +++ --- |
12 files changed, 487 insertions(+), 63 deletions(-)
diff --git a/ include/based/concepts/is/castable.hpp b/ include/based/concepts/is/castable.hpp
@@ -0,0 +1,11 @@
#pragma once
#include "based/utility/declvar.hpp"
namespace based
{
template<class From, class To>
concept CastableTo = requires { static_cast<To>(declval<From>()); };
} // namespace based
diff --git a/ include/based/types/limits.hpp b/ include/based/types/limits.hpp
@@ -0,0 +1,95 @@
#pragma once
#include "based/types/types.hpp"
namespace based
{
template<class T>
struct limits;
// Signed
template<>
struct limits<i8>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = true;
static constexpr auto min = i8::basic_cast(0x80);
static constexpr auto max = i8::basic_cast(0x7F);
};
template<>
struct limits<i16>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = true;
static constexpr auto min = i16::basic_cast(0x8000);
static constexpr auto max = i16::basic_cast(0x7FFF);
};
template<>
struct limits<i32>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = true;
static constexpr auto min = i32::basic_cast(0x80000000);
static constexpr auto max = i32::basic_cast(0x7FFFFFFF);
};
template<>
struct limits<i64>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = true;
static constexpr auto min = i64::basic_cast(0x8000000000000000);
static constexpr auto max = i64::basic_cast(0x7FFFFFFFFFFFFFFF);
};
// Unsigned
template<>
struct limits<u8>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = false;
static constexpr auto min = u8::basic_cast(0x00);
static constexpr auto max = u8::basic_cast(0xFF);
};
template<>
struct limits<u16>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = false;
static constexpr auto min = u16::basic_cast(0x0000);
static constexpr auto max = u16::basic_cast(0xFFFF);
};
template<>
struct limits<u32>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = false;
static constexpr auto min = u32::basic_cast(0x00000000);
static constexpr auto max = u32::basic_cast(0xFFFFFFFF);
};
template<>
struct limits<u64>
{
static constexpr bool is_integer = true;
static constexpr bool is_signed = false;
static constexpr auto min = u64::basic_cast(0x0000000000000000);
static constexpr auto max = u64::basic_cast(0xFFFFFFFFFFFFFFFF);
};
} // namespace based
diff --git a/ include/based/types/literals.hpp b/ include/based/types/literals.hpp
@@ -0,0 +1,170 @@
#pragma once
#include "based/char/is/digit.hpp"
#include "based/concepts/is/castable.hpp"
#include "based/types/limits.hpp"
#include "based/types/types.hpp"
// NOLINTBEGIN(google-runtime-int)
namespace based
{
// Signed
namespace detail
{
template<signed long long val>
constexpr auto make_signed_itnernal()
{
if constexpr (val <= limits<i8>::max.value) {
return i8::basic_cast(val);
} else if constexpr (val <= limits<i16>::max.value) {
return i16::basic_cast(val);
} else if constexpr (val <= limits<i32>::max.value) {
return i32::basic_cast(val);
} else {
return i64::basic_cast(val);
}
}
template<signed long long v, char c, char... cs>
constexpr auto make_signed_itnernal()
{
const signed long long radix = 10;
static_assert(is_digit(c), "invalid digit");
static_assert(v <= (limits<i64>::max.value - (c - '0')) / radix, "overflow");
return make_signed_itnernal<(radix * v) + (c - '0'), cs...>();
}
template<char... cs>
constexpr auto make_signed()
{
return make_signed_itnernal<0, cs...>();
}
} // namespace detail
namespace literals
{
template<char... cs>
auto operator"" _i()
{
return detail::make_signed<cs...>();
}
template<char... cs>
requires CastableTo<decltype(detail::make_signed<cs...>()), u8>
consteval auto operator"" _i8()
{
return static_cast<u8>(detail::make_signed<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_signed<cs...>()), u16>
consteval auto operator"" _i16()
{
return static_cast<u16>(detail::make_signed<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_signed<cs...>()), u32>
consteval auto operator"" _i32()
{
return static_cast<u32>(detail::make_signed<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_signed<cs...>()), u64>
consteval auto operator"" _i64()
{
return static_cast<u64>(detail::make_signed<cs...>());
}
} // namespace literals
// Unsigned
namespace detail
{
template<unsigned long long val>
constexpr auto make_unsigned_internal()
{
if constexpr (val <= limits<u8>::max.value) {
return u8::basic_cast(val);
} else if constexpr (val <= limits<u16>::max.value) {
return u16::basic_cast(val);
} else if constexpr (val <= limits<u32>::max.value) {
return u32::basic_cast(val);
} else {
return u64::basic_cast(val);
}
}
template<unsigned long long v, char c, char... cs>
constexpr auto make_unsigned_internal()
{
const unsigned long long radix = 10;
static_assert(is_digit(c), "invalid digit");
static_assert(v <= (limits<u64>::max.value - (c - '0')) / radix, "overflow");
return make_unsigned_internal<(radix * v) + c - '0', cs...>();
}
template<char... cs>
constexpr auto make_unsigned()
{
return make_unsigned_internal<0, cs...>();
}
} // namespace detail
namespace literals
{
template<char... cs>
auto operator"" _u()
{
return detail::make_unsigned<cs...>();
}
template<char... cs>
requires CastableTo<decltype(detail::make_unsigned<cs...>()), u8>
consteval auto operator"" _u8()
{
return static_cast<u8>(detail::make_unsigned<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_unsigned<cs...>()), u16>
consteval auto operator"" _u16()
{
return static_cast<u16>(detail::make_unsigned<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_unsigned<cs...>()), u32>
consteval auto operator"" _u32()
{
return static_cast<u32>(detail::make_unsigned<cs...>());
}
template<char... cs>
requires CastableTo<decltype(detail::make_unsigned<cs...>()), u64>
consteval auto operator"" _u64()
{
return static_cast<u64>(detail::make_unsigned<cs...>());
}
} // namespace literals
using namespace literals; // NOLINT(*namespace*)
} // namespace based
// NOLINTEND(google-runtime-int)
diff --git a/ include/based/types/strong.hpp b/ include/based/types/strong.hpp
@@ -20,6 +20,8 @@
struct strong_type
using basic_type = V;
using tag_type = Tag;
basic_type value;
constexpr ~strong_type() = default;
constexpr explicit strong_type()
@@ -40,7 +42,11 @@
struct strong_type
constexpr strong_type& operator=(const strong_type&) = default;
constexpr strong_type& operator=(strong_type&&) = default;
basic_type value;
template<class T>
static constexpr Tag basic_cast(T value)
{
return Tag {static_cast<basic_type>(value)};
}
};
// NOLINTEND(*crtp*)
@@ -276,6 +282,23 @@
constexpr auto operator~(LHS lhs)
}
template<class LHS>
concept unariable = requires(LHS lhs) { unary(lhs); };
template<class LHS>
requires unariable<LHS>
constexpr auto operator+(LHS lhs)
{
return decltype(lhs)(+lhs.value);
}
template<class LHS>
requires unariable<LHS>
constexpr auto operator-(LHS lhs)
{
return decltype(lhs)(-lhs.value);
}
template<class LHS>
concept preincable = requires(LHS lhs) { preinc(lhs); };
template<class LHS>
diff --git a/ include/based/types/types.hpp b/ include/based/types/types.hpp
@@ -1,4 +1,5 @@
#pragma once
#include "based/macro/foreach_1.hpp"
#include "based/types/strong.hpp"
@@ -10,41 +11,80 @@
namespace based
using bi8 = signed char;
using bi16 = signed short int;
using bi32 = signed int;
using bi64 = signed long int;
using bi64 = signed long long int;
using bu8 = unsigned char;
using bu16 = unsigned short int;
using bu32 = unsigned int;
using bu64 = unsigned long int;
using bu64 = unsigned long long int;
using size_t = bu64;
#define BASED_DETAIL_TYPE(Name) \
/* NOLINTNEXTLINE(*macro*) */ \
struct Name : strong_type<b##Name, Name> \
{ \
using strong_type::strong_type; \
using strong_type::operator=; \
}; \
\
namespace literals \
{ \
constexpr auto operator""_##Name(unsigned long long val) \
{ \
/* NOLINTNEXTLINE(*macro*) */ \
return Name {static_cast<Name::basic_type>(val)}; \
} \
} // namespace literals
BASED_DETAIL_TYPE(i8)
BASED_DETAIL_TYPE(i16)
BASED_DETAIL_TYPE(i32)
BASED_DETAIL_TYPE(i64)
BASED_DETAIL_TYPE(u8)
BASED_DETAIL_TYPE(u16)
BASED_DETAIL_TYPE(u32)
BASED_DETAIL_TYPE(u64)
struct i64 : strong_type<signed long long int, i64>
{
using strong_type::strong_type;
using strong_type::operator=;
};
struct i32 : strong_type<signed int, i32>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator i64() { return i64::basic_cast(value); }
};
struct i16 : strong_type<signed short int, i16>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator i64() { return i64::basic_cast(value); }
explicit constexpr operator i32() { return i32::basic_cast(value); }
};
struct i8 : strong_type<signed char, i8>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator i64() { return i64::basic_cast(value); }
explicit constexpr operator i32() { return i32::basic_cast(value); }
explicit constexpr operator i16() { return i16::basic_cast(value); }
};
struct u64 : strong_type<unsigned long long int, u64>
{
using strong_type::strong_type;
using strong_type::operator=;
};
struct u32 : strong_type<unsigned int, u32>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator u64() { return u64::basic_cast(value); }
};
struct u16 : strong_type<unsigned short int, u16>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator u64() { return u64::basic_cast(value); }
explicit constexpr operator u32() { return u32::basic_cast(value); }
};
struct u8 : strong_type<unsigned char, u8>
{
using strong_type::strong_type;
using strong_type::operator=;
explicit constexpr operator u64() { return u64::basic_cast(value); }
explicit constexpr operator u32() { return u32::basic_cast(value); }
explicit constexpr operator u16() { return u16::basic_cast(value); }
};
#define BASED_DETAIL_OP_UNARY(Prefix, Name, Index) \
auto Name(Prefix##8)->Prefix##8; \
@@ -73,7 +113,9 @@
BASED_DETAIL_TYPE(u64)
auto Name(Prefix##64, Prefix##32)->Prefix##64; \
auto Name(Prefix##64, Prefix##64)->Prefix##64;
BASED_FOREACH_1(i, BASED_DETAIL_OP_UNARY, preinc, postinc, predec, postdec)
BASED_FOREACH_1(
i, BASED_DETAIL_OP_UNARY, unary, preinc, postinc, predec, postdec
)
BASED_FOREACH_1(
i, BASED_DETAIL_OP_BINARY, compare, order, add, sub, mul, div, mod
)
diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt
@@ -29,6 +29,8 @@
endfunction()
add_test(types strong_type_test)
add_test(types type_test)
add_test(types limits)
add_test(types literals)
## ----- Trait -----
diff --git a/ test/source/list_test.cpp b/ test/source/list_test.cpp
@@ -4,13 +4,15 @@
#include <catch2/catch_test_macros.hpp>
#include "based/types/types.hpp"
#include "based/types/literals.hpp"
template class based::list_pool<based::u8, based::u8>;
// NOLINTBEGIN(*complexity*)
TEST_CASE("list_pool", "[list/list_pool]")
{
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using list_pool = based::list_pool<based::u8, based::u8>;
auto pool = list_pool();
@@ -65,13 +67,13 @@
TEST_CASE("list_pool", "[list/list_pool]")
TEST_CASE("list_pool iterator", "[list/list_pool]")
{
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using list_pool = based::list_pool<based::u8, based::u8>;
auto pool = list_pool();
auto head = pool.node_empty();
static constexpr auto iter_count = 0xFF_u8;
static constexpr auto iter_count = 255_u8;
for (auto i = 0_u8; i < iter_count; i++) {
head = pool.allocate(i, head);
}
@@ -86,7 +88,7 @@
TEST_CASE("list_pool iterator", "[list/list_pool]")
sum += *it;
}
REQUIRE(sum == 0xFF_u32 * 0xFE_u32);
REQUIRE(sum == 255_u32 * 254_u32);
}
SECTION("accumulate")
@@ -103,7 +105,7 @@
TEST_CASE("list_pool iterator", "[list/list_pool]")
}
);
REQUIRE(sum == 0xFF_u32 * 0xFE_u32 / 2_u32);
REQUIRE(sum == 255_u32 * 254_u32 / 2_u32);
}
based::free_list(pool, head);
@@ -111,13 +113,13 @@
TEST_CASE("list_pool iterator", "[list/list_pool]")
TEST_CASE("list_pool const iterator", "[list/list_pool]")
{
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using list_pool = based::list_pool<based::u8, based::u8>;
auto pool = list_pool();
auto head = pool.node_empty();
static constexpr auto iter_count = 0xFF_u8;
static constexpr auto iter_count = 255_u8;
for (auto i = 0_u8; i < iter_count; i++) {
head = pool.allocate(i, head);
}
@@ -132,7 +134,7 @@
TEST_CASE("list_pool const iterator", "[list/list_pool]")
sum += *it;
}
REQUIRE(sum == 0xFF_u32 * 0xFE_u32);
REQUIRE(sum == 255_u32 * 254_u32);
}
SECTION("const accumulate")
@@ -153,7 +155,7 @@
TEST_CASE("list_pool const iterator", "[list/list_pool]")
);
};
REQUIRE(sum(pool, head) == 0xFF_u32 * 0xFE_u32 / 2_u32);
REQUIRE(sum(pool, head) == 255_u32 * 254_u32 / 2_u32);
}
based::free_list(pool, head);
@@ -177,26 +179,31 @@
TEST_CASE("list_pool queue", "[list/list_pool/queue]")
REQUIRE(pool.pop_front(queue) == queue);
}
using namespace based::literals; // NOLINT
static constexpr auto iter_count = 0xFF_u8;
for (auto i = 0_u8; i < iter_count; i++) {
if (i % 2_u8 == 0_u8) {
queue = pool.push_front(queue, i);
} else {
queue = pool.push_back(queue, i);
SECTION("operation")
{
using namespace based::literals; // NOLINT(*namespace*)
static constexpr auto iter_count = 255_u8;
for (auto i = 0_u8; i < iter_count; i++) {
if (i % 2_u8 == 0_u8) {
queue = pool.push_front(queue, i);
} else {
queue = pool.push_back(queue, i);
}
if (i % 3_u8 == 0_u8) {
queue = pool.pop_front(queue);
}
}
if (i % 3_u8 == 0_u8) {
queue = pool.pop_front(queue);
auto sum = 0_u64;
for (auto it = iter(pool, queue.first); it != iter(pool); ++it) {
sum += *it;
}
}
auto sum = 0_u64;
for (auto it = iter(pool, queue.first); it != iter(pool); ++it) {
sum += *it;
}
pool.free(queue);
pool.free(queue);
REQUIRE(sum == 21717_u64);
REQUIRE(sum == 21717_u64);
}
}
// NOLINTEND(*complexity*)
diff --git a/ test/source/types/limits.cpp b/ test/source/types/limits.cpp
@@ -0,0 +1,33 @@
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include "based/types/limits.hpp"
#include <catch2/catch_test_macros.hpp>
using based::limits;
TEST_CASE("unsigned", "[types/literals]")
{
STATIC_REQUIRE(limits<based::u8>::min.value == 0ULL);
STATIC_REQUIRE(limits<based::u16>::min.value == 0ULL);
STATIC_REQUIRE(limits<based::u32>::min.value == 0ULL);
STATIC_REQUIRE(limits<based::u64>::min.value == 0ULL);
STATIC_REQUIRE(limits<based::u8>::max.value == 255ULL);
STATIC_REQUIRE(limits<based::u16>::max.value == 65535ULL);
STATIC_REQUIRE(limits<based::u32>::max.value == 4294967295ULL);
STATIC_REQUIRE(limits<based::u64>::max.value == 18446744073709551615ULL);
}
TEST_CASE("signed", "[types/literals]")
{
STATIC_REQUIRE(limits<based::i8>::min.value == -128LL);
STATIC_REQUIRE(limits<based::i16>::min.value == -32768LL);
STATIC_REQUIRE(limits<based::i32>::min.value == -2147483648LL);
STATIC_REQUIRE(limits<based::i64>::min.value == -9223372036854775807LL - 1);
STATIC_REQUIRE(limits<based::i8>::max.value == 127LL);
STATIC_REQUIRE(limits<based::i16>::max.value == 32767LL);
STATIC_REQUIRE(limits<based::i32>::max.value == 2147483647LL);
STATIC_REQUIRE(limits<based::i64>::max.value == 9223372036854775807LL);
}
diff --git a/ test/source/types/literals.cpp b/ test/source/types/literals.cpp
@@ -0,0 +1,41 @@
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include "based/types/literals.hpp"
#include <catch2/catch_test_macros.hpp>
#include "based/concepts/is/same.hpp"
using namespace based::literals; // NOLINT(*namespace*)
using based::SameAs;
TEST_CASE("unsigned", "[types/literals]")
{
STATIC_REQUIRE(SameAs<decltype(0_u), based::u8>);
STATIC_REQUIRE(SameAs<decltype(255_u), based::u8>);
STATIC_REQUIRE(SameAs<decltype(256_u), based::u16>);
STATIC_REQUIRE(SameAs<decltype(65535_u), based::u16>);
STATIC_REQUIRE(SameAs<decltype(65536_u), based::u32>);
STATIC_REQUIRE(SameAs<decltype(4294967295_u), based::u32>);
STATIC_REQUIRE(SameAs<decltype(4294967296_u), based::u64>);
STATIC_REQUIRE(SameAs<decltype(18446744073709551615_u), based::u64>);
}
TEST_CASE("signed", "[types/literals]")
{
STATIC_REQUIRE(SameAs<decltype(0_i), based::i8>);
STATIC_REQUIRE(SameAs<decltype(127_i), based::i8>);
STATIC_REQUIRE(SameAs<decltype(128_i), based::i16>);
STATIC_REQUIRE(SameAs<decltype(32767_i), based::i16>);
STATIC_REQUIRE(SameAs<decltype(2147483647_i), based::i32>);
STATIC_REQUIRE(SameAs<decltype(2147483648_i), based::i64>);
STATIC_REQUIRE(SameAs<decltype(9223372036854775807_i), based::i64>);
STATIC_REQUIRE(SameAs<decltype(-127_i), based::i8>);
STATIC_REQUIRE(SameAs<decltype(-128_i), based::i16>);
STATIC_REQUIRE(SameAs<decltype(-32767_i), based::i16>);
STATIC_REQUIRE(SameAs<decltype(-2147483647_i), based::i32>);
STATIC_REQUIRE(SameAs<decltype(-2147483648_i), based::i64>);
STATIC_REQUIRE(SameAs<decltype(-9223372036854775807_i), based::i64>);
}
diff --git a/ test/source/types/strong_type_test.cpp b/ test/source/types/strong_type_test.cpp
@@ -4,7 +4,7 @@
#include <catch2/catch_test_macros.hpp>
#include "based/types/types.hpp"
#include "based/types/literals.hpp"
struct t1 : based::strong_type<based::u8, t1>
{
@@ -34,7 +34,7 @@
TEST_CASE("strong_type", "[types/strong_type]")
STATIC_REQUIRE(based::addable<t1, t2>);
STATIC_REQUIRE(based::addable<t2, t1>);
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
REQUIRE(t1 {10_u8} + t1 {20_u8} == t1 {30_u8});
REQUIRE(t1 {10_u8} + t2 {20_u8} == t1 {30_u8});
REQUIRE(t2 {10_u8} + t1 {20_u8} == t2 {30_u8});
diff --git a/ test/source/types/type_test.cpp b/ test/source/types/type_test.cpp
@@ -1,4 +1,4 @@
// #define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE
#include <catch2/catch_test_macros.hpp>
diff --git a/ test/source/utility/buffer_test.cpp b/ test/source/utility/buffer_test.cpp
@@ -4,7 +4,7 @@
#include <catch2/catch_test_macros.hpp>
#include "based/types/types.hpp"
#include "based/types/literals.hpp"
template struct based::buffer<sizeof(void*)>;
@@ -31,7 +31,7 @@
TEST_CASE("valid type", "[template/buffer]")
TEST_CASE("buffer", "[template/buffer]")
{
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using buffer = based::buffer<sizeof(based::size_t)>;
static constexpr auto value = 8_u8;
@@ -60,7 +60,7 @@
TEST_CASE("buffer", "[template/buffer]")
TEST_CASE("const buffer", "[template/buffer]")
{
using namespace based::literals; // NOLINT
using namespace based::literals; // NOLINT(*namespace*)
using buffer = based::buffer<sizeof(based::size_t)>;
static constexpr auto value = 8_u8;