7 Commits

Author SHA1 Message Date
afbfd0e781 Add numeric library 2025-04-03 23:27:39 +02:00
0776012d09 Add bit library 2025-04-03 00:34:54 +02:00
4f8cbd442a Add invoke_r & co 2025-03-26 21:52:00 +01:00
2d309a2cff Separate allocator from memory 2025-03-26 21:35:18 +01:00
f19d93a69a Improve implementation of invoke 2025-03-26 18:54:54 +01:00
e034efe8bd Add member pointer stuff to meta lib 2025-03-26 18:54:53 +01:00
2c457c4275 Add address_of 2025-03-26 18:54:49 +01:00
19 changed files with 678 additions and 101 deletions

View File

@ -11,12 +11,14 @@ cc_library(
hdrs = [
"annotations.hpp",
"assert.hpp",
"bit.hpp",
"config.hpp",
"defer.hpp",
"float.hpp",
"functional.hpp",
"integers.hpp",
"meta.hpp",
"numeric.hpp",
"utility.hpp",
],
srcs = [
@ -37,10 +39,12 @@ cc_library(
"//asl/types:box",
],
) for name in [
"bit",
"defer",
"float",
"functional",
"integers",
"meta",
"numeric",
"utility",
]]

166
asl/base/bit.hpp Normal file
View File

@ -0,0 +1,166 @@
// Copyright 2025 Steven Le Rouzic
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include "asl/base/integers.hpp"
#include "asl/base/meta.hpp"
namespace asl
{
constexpr bool has_single_bit(is_unsigned_integer auto x)
{
return x != 0 && ((x - 1) & x) == 0;
}
constexpr int popcount(uint8_t v)
{
v = v - ((v >> 1) & 0x55);
v = (v & 0x33) + ((v >> 2) & 0x33);
return (v + (v >> 4)) & 0x0F;
}
constexpr int popcount(uint16_t v)
{
v = v - ((v >> 1) & 0x5555);
v = (v & 0x3333) + ((v >> 2) & 0x3333);
return static_cast<uint16_t>((v + (v >> 4) & 0x0F0F) * uint16_t{0x0101}) >> 8;
}
constexpr int popcount(uint32_t v)
{
v = v - ((v >> 1) & 0x5555'5555);
v = (v & 0x3333'3333) + ((v >> 2) & 0x3333'3333);
return static_cast<int>(((v + (v >> 4) & 0x0F0F'0F0F) * 0x0101'0101) >> 24);
}
constexpr int popcount(uint64_t v)
{
v = v - ((v >> 1) & 0x5555'5555'5555'5555);
v = (v & 0x3333'3333'3333'3333) + ((v >> 2) & 0x3333'3333'3333'3333);
return static_cast<int>(((v + (v >> 4) & 0x0F0F'0F0F'0F0F'0F0F) * 0x0101'0101'0101'0101) >> 56);
}
constexpr uint8_t propagate_right_one(uint8_t v)
{
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
return v;
}
constexpr uint16_t propagate_right_one(uint16_t v)
{
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
return v;
}
constexpr uint32_t propagate_right_one(uint32_t v)
{
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
v = v | (v >> 16);
return v;
}
constexpr uint64_t propagate_right_one(uint64_t v)
{
v = v | (v >> 1);
v = v | (v >> 2);
v = v | (v >> 4);
v = v | (v >> 8);
v = v | (v >> 16);
v = v | (v >> 32);
return v;
}
constexpr int countr_zero(is_unsigned_integer auto v)
{
v = ~v & (v - 1);
return popcount(v);
}
constexpr int countr_one(is_unsigned_integer auto v)
{
v = v & (~v - 1);
return popcount(v);
}
constexpr int countl_zero(is_unsigned_integer auto v)
{
v = ~propagate_right_one(v);
return popcount(v);
}
constexpr int countl_one(is_unsigned_integer auto v)
{
v = ~v;
v = ~propagate_right_one(v);
return popcount(v);
}
template<is_unsigned_integer T>
constexpr T rotr(T v, int s);
template<is_unsigned_integer T>
constexpr T rotl(T v, int s) // NOLINT(*-no-recursion)
{
static constexpr int N = sizeof(decltype(v)) * 8;
s = s % N;
return (s >= 0) ? (v << s) | (v >> (N - s)) : rotr(v, -s);
}
template<is_unsigned_integer T>
constexpr T rotr(T v, int s) // NOLINT(*-no-recursion)
{
static constexpr int N = sizeof(decltype(v)) * 8;
s = s % N;
return (s >= 0) ? (v >> s) | (v << (N - s)) : rotl(v, -s);
}
constexpr uint16_t byteswap(uint16_t v)
{
return static_cast<uint16_t>((v << 8U) | (v >> 8U));
}
constexpr uint32_t byteswap(uint32_t v)
{
return rotr(v & 0x00ff'00ff, 8) | rotl(v & 0xff00'ff00, 8);
}
constexpr uint64_t byteswap(uint64_t v)
{
return (uint64_t{byteswap(static_cast<uint32_t>(v))} << 32)
| uint64_t{byteswap(static_cast<uint32_t>(v >> 32))};
}
constexpr auto bit_ceil(is_unsigned_integer auto v)
{
v -= 1;
v = propagate_right_one(v);
v += 1;
return v;
}
constexpr auto bit_floor(is_unsigned_integer auto v)
{
v = propagate_right_one(v);
v = v - (v >> 1);
return v;
}
constexpr int bit_width(is_unsigned_integer auto v)
{
static constexpr int N = sizeof(decltype(v)) * 8;
return N - countl_zero(v);
}
} // namespace asl

194
asl/base/bit_tests.cpp Normal file
View File

@ -0,0 +1,194 @@
// Copyright 2025 Steven Le Rouzic
//
// SPDX-License-Identifier: BSD-3-Clause
#include "asl/base/bit.hpp"
#include "asl/testing/testing.hpp"
ASL_TEST(has_single_bit)
{
ASL_TEST_EXPECT(asl::has_single_bit(4U));
ASL_TEST_EXPECT(asl::has_single_bit(1024U));
ASL_TEST_EXPECT(asl::has_single_bit(0x8000'0000U));
ASL_TEST_EXPECT(asl::has_single_bit(0x0000'8000'0000'0000ULL));
ASL_TEST_EXPECT(!asl::has_single_bit(0U));
ASL_TEST_EXPECT(!asl::has_single_bit(3U));
ASL_TEST_EXPECT(!asl::has_single_bit(341U));
}
ASL_TEST(popcount) // NOLINT(*-cognitive-complexity)
{
ASL_TEST_EXPECT(asl::popcount(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::popcount(uint8_t{255}) == 8);
ASL_TEST_EXPECT(asl::popcount(uint8_t{46}) == 4);
ASL_TEST_EXPECT(asl::popcount(uint16_t{0}) == 0);
ASL_TEST_EXPECT(asl::popcount(uint16_t{255}) == 8);
ASL_TEST_EXPECT(asl::popcount(uint16_t{65535}) == 16);
ASL_TEST_EXPECT(asl::popcount(uint16_t{46}) == 4);
ASL_TEST_EXPECT(asl::popcount(uint32_t{0}) == 0);
ASL_TEST_EXPECT(asl::popcount(uint32_t{255}) == 8);
ASL_TEST_EXPECT(asl::popcount(uint32_t{65535}) == 16);
ASL_TEST_EXPECT(asl::popcount(uint32_t{65536}) == 1);
ASL_TEST_EXPECT(asl::popcount(uint32_t{46}) == 4);
ASL_TEST_EXPECT(asl::popcount(uint32_t{0xffff'ffff}) == 32);
ASL_TEST_EXPECT(asl::popcount(0x8421'1248'8421'1248) == 16);
ASL_TEST_EXPECT(asl::popcount(0xffff'ffff'ffff'ffff) == 64);
}
ASL_TEST(countr_zero)
{
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0}) == 8);
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{255}) == 0);
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b00011100}) == 2);
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b10101010}) == 1);
ASL_TEST_EXPECT(asl::countr_zero(uint8_t{0b10101001}) == 0);
}
ASL_TEST(countr_one)
{
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::countr_one(uint8_t{255}) == 8);
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b00011100}) == 0);
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b10101011}) == 2);
ASL_TEST_EXPECT(asl::countr_one(uint8_t{0b10101001}) == 1);
}
ASL_TEST(countl_zero)
{
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0}) == 8);
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{255}) == 0);
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b00011100}) == 3);
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b10101010}) == 0);
ASL_TEST_EXPECT(asl::countl_zero(uint8_t{0b00001001}) == 4);
}
ASL_TEST(countl_one)
{
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::countl_one(uint8_t{255}) == 8);
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b00011100}) == 0);
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b10101010}) == 1);
ASL_TEST_EXPECT(asl::countl_one(uint8_t{0b11101001}) == 3);
}
ASL_TEST(rotl)
{
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 1) == 0x8000'0200);
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 3) == 0x0000'0802);
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, 0) == 0x4000'0100);
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, -1) == 0x2000'0080);
ASL_TEST_EXPECT(asl::rotl(uint32_t{0x4000'0100}, -12) == 0x1004'0000);
}
ASL_TEST(rotr)
{
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, -1) == 0x8000'0200);
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, -3) == 0x0000'0802);
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 0) == 0x4000'0100);
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 1) == 0x2000'0080);
ASL_TEST_EXPECT(asl::rotr(uint32_t{0x4000'0100}, 12) == 0x1004'0000);
}
ASL_TEST(byteswap)
{
ASL_TEST_EXPECT(asl::byteswap(uint32_t{0x1234'5678}) == 0x7856'3412);
ASL_TEST_EXPECT(asl::byteswap(uint16_t{0x1234}) == 0x3412);
ASL_TEST_EXPECT(asl::byteswap(uint64_t{0x0123'4567'89ab'cdef}) == 0xefcd'ab89'6745'2301);
}
ASL_TEST(bit_ceil) // NOLINT(*-cognitive-complexity)
{
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{3}) == 4);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{4}) == 4);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{5}) == 8);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{6}) == 8);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{127}) == 128);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{128}) == 128);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{254}) == 0);
ASL_TEST_EXPECT(asl::bit_ceil(uint8_t{255}) == 0);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{3}) == 4);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{4}) == 4);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{5}) == 8);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{6}) == 8);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{32000}) == 32768);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{32768}) == 32768);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{35000}) == 0);
ASL_TEST_EXPECT(asl::bit_ceil(uint16_t{65535}) == 0);
}
ASL_TEST(bit_floor) // NOLINT(*-cognitive-complexity)
{
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{4}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{5}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{6}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{127}) == 64);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{128}) == 128);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{254}) == 128);
ASL_TEST_EXPECT(asl::bit_floor(uint8_t{255}) == 128);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{4}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{5}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{6}) == 4);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{32000}) == 16384);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{32768}) == 32768);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{35000}) == 32768);
ASL_TEST_EXPECT(asl::bit_floor(uint16_t{65535}) == 32768);
}
ASL_TEST(bit_width) // NOLINT(*-cognitive-complexity)
{
ASL_TEST_EXPECT(asl::bit_width(uint8_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{4}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{5}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint8_t{6}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{4}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{5}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{6}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint16_t{65535}) == 16);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{4}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{5}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{6}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{65535}) == 16);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{65536}) == 17);
ASL_TEST_EXPECT(asl::bit_width(uint32_t{0xffff'ffff}) == 32);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{0}) == 0);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{1}) == 1);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{2}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{3}) == 2);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{4}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{5}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{6}) == 3);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{65535}) == 16);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{65536}) == 17);
ASL_TEST_EXPECT(asl::bit_width(uint64_t{0xffff'ffff}) == 32);
}

View File

@ -6,6 +6,9 @@
#include "asl/base/meta.hpp"
using float32_t = float;
using float64_t = double;
namespace asl
{

View File

@ -9,56 +9,62 @@
namespace asl {
template<typename... Args, typename C>
constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args)
-> decltype((self.*f)(std::forward<Args>(args)...))
requires requires {
(self.*f)(std::forward<Args>(args)...);
template<typename F, typename... Args>
constexpr auto invoke(F&& f, Args&&... args)
-> decltype(std::forward<F>(f)(std::forward<Args>(args)...))
requires (!is_member_ptr<un_cvref_t<F>>) && requires {
f(std::forward<Args>(args)...);
}
{
return std::forward<F>(f)(std::forward<Args>(args)...);
}
template<typename C>
constexpr auto&& invoke(auto C::* f, same_or_derived_from<C> auto&& arg)
{
return std::forward<decltype(arg)>(arg).*f;
}
template<typename C>
constexpr auto&& invoke(auto C::* f, auto&& arg)
requires (
!same_or_derived_from<decltype(arg), C>
&& requires { (*arg).*f; }
)
{
return (*std::forward<decltype(arg)>(arg)).*f;
}
template<typename C, typename... Args>
constexpr auto invoke(is_func auto C::* f, same_or_derived_from<C> auto&& self, Args&&... args)
-> decltype((std::forward<decltype(self)>(self).*f)(std::forward<Args>(args)...))
requires requires { (self.*f)(std::forward<Args>(args)...); }
{
return (std::forward<decltype(self)>(self).*f)(std::forward<Args>(args)...);
}
template<typename... Args, typename C>
constexpr auto invoke(is_func auto C::* f, auto* self, Args&&... args)
-> decltype((self->*f)(std::forward<Args>(args)...))
requires requires {
(self->*f)(std::forward<Args>(args)...);
}
{
return (self->*f)(std::forward<Args>(args)...);
}
template<typename... Args, typename C>
constexpr auto invoke(is_object auto C::* m, auto&& self, Args&&...)
-> decltype(self.*m)
template<typename C, typename... Args>
constexpr auto invoke(is_func auto C::* f, auto&& self, Args&&... args)
-> decltype(((*std::forward<decltype(self)>(self)).*f)(std::forward<Args>(args)...))
requires (
sizeof...(Args) == 0 &&
requires { self.*m; }
!same_or_derived_from<decltype(self), C>
&& requires { ((*self).*f)(std::forward<Args>(args)...); }
)
{
return std::forward<decltype(self)>(self).*m;
return ((*std::forward<decltype(self)>(self)).*f)(std::forward<Args>(args)...);
}
template<typename... Args, typename C>
constexpr auto invoke(is_object auto C::* m, auto* self, Args&&...)
-> decltype(self->*m)
requires (
sizeof...(Args) == 0 &&
requires { self->*m; }
)
template<typename R, typename F, typename... Args>
constexpr R invoke_r(F&& f, Args&&... args)
{
return self->*m;
}
template<typename... Args>
constexpr auto invoke(auto&& f, Args&&... args)
-> decltype(f(std::forward<Args>(args)...))
requires requires {
f(std::forward<Args>(args)...);
if constexpr (is_void<R>)
{
static_cast<void>(invoke(std::forward<F>(f), std::forward<Args>(args)...));
}
else
{
return invoke(std::forward<F>(f), std::forward<Args>(args)...);
}
{
return std::forward<decltype(f)>(f)(std::forward<Args>(args)...);
}
template<typename Void, typename F, typename... Args>
@ -79,4 +85,8 @@ concept invocable = requires (F&& f, Args&&... args)
invoke(std::forward<F>(f), std::forward<Args>(args)...);
};
template<typename R, typename F, typename... Args>
concept invocable_r = invocable<F, Args...>
&& (is_void<R> || convertible_to<invoke_result_t<F, Args...>, R>);
} // namespace asl

View File

@ -7,9 +7,12 @@
struct HasFunction
{
void do_something(int, float) {}
void do_something(int, float) const {}
int& do_something2(int, float) &;
};
struct HasFunction2 : public HasFunction {};
struct HasMember
{
int member{};
@ -17,6 +20,8 @@ struct HasMember
void (*member_fn)(){};
};
struct HasMember2 : public HasMember {};
struct Functor
{
int64_t operator()() { return 35; }
@ -34,7 +39,13 @@ static_assert(asl::same_as<asl::invoke_result_t<Functor>, int64_t>);
static_assert(asl::same_as<asl::invoke_result_t<Functor, int>, int>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(static_cast<float(*)(float)>(some_func1)), float>, float>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), HasFunction, int, float>, void>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember>, const int&>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), const HasFunction2&, int, float>, void>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something), HasFunction*, int, float>, void>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something2), HasFunction2&, int, float>, int&>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasFunction::do_something2), HasFunction*, int, float>, int&>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), HasMember>, int&&>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember&>, const int&>);
static_assert(asl::same_as<asl::invoke_result_t<decltype(&HasMember::member), const HasMember2*>, const int&>);
static_assert(asl::invocable<int()>);
static_assert(!asl::invocable<int(), int>);
@ -45,8 +56,20 @@ static_assert(asl::invocable<Functor, int>);
static_assert(!asl::invocable<Functor, void*>);
static_assert(asl::invocable<decltype(static_cast<float(*)(float)>(some_func1)), float>);
static_assert(asl::invocable<decltype(&HasFunction::do_something), HasFunction, int, float>);
static_assert(asl::invocable<decltype(&HasFunction::do_something), const HasFunction2&, int, float>);
static_assert(asl::invocable<decltype(&HasFunction::do_something), HasFunction*, int, float>);
static_assert(!asl::invocable<decltype(&HasFunction::do_something), HasFunction, int, int*>);
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember>);
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), HasFunction, int, float>);
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), const HasFunction2&, int, float>);
static_assert(asl::invocable<decltype(&HasFunction::do_something2), HasFunction2&, int, float>);
static_assert(asl::invocable<decltype(&HasFunction::do_something2), HasFunction*, int, float>);
static_assert(!asl::invocable<decltype(&HasFunction::do_something2), HasFunction, int, int*>);
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember2>);
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember&>);
static_assert(asl::invocable<decltype(&HasMember::member), const HasMember2*>);
static_assert(asl::invocable_r<void*, int*()>);
static_assert(!asl::invocable_r<int*, void*()>);
ASL_TEST(invoke_member_function)
{
@ -89,3 +112,9 @@ ASL_TEST(invoke_lambda)
ASL_TEST_EXPECT(asl::invoke([](){ return 35; }) == 35);
ASL_TEST_EXPECT(asl::invoke([](int x){ return x + 2; }, 6) == 8);
}
ASL_TEST(invoke_r)
{
ASL_TEST_EXPECT(asl::invoke_r<int>([]() { return 1ULL; }) == 1);
asl::invoke_r<void>([]() { return 1ULL; });
}

View File

@ -25,6 +25,9 @@ struct empty {};
template<typename T> struct id { using type = T; };
struct in_place_t {};
static constexpr in_place_t in_place{};
template<typename... Args> static constexpr isize_t types_count = sizeof...(Args);
template<typename T, T kValue> struct integral_constant { static constexpr T value = kValue; };
@ -92,12 +95,6 @@ template<typename T> concept trivially_destructible = __is_trivially_destructibl
template<typename T> concept copyable = copy_constructible<T> && copy_assignable<T>;
template<typename T> concept moveable = move_constructible<T> && move_assignable<T>;
template<typename From, typename To>
concept convertible_to = __is_convertible(From, To);
template<typename Derived, class Base>
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_to<const volatile Derived*, const volatile Base*>;
using nullptr_t = decltype(nullptr);
template<typename T> struct _un_const_helper { using type = T; };
@ -154,6 +151,15 @@ template<typename T> struct _is_ptr_helper<T*> : true_type {};
template<typename T> concept is_ptr = _is_ptr_helper<un_cv_t<T>>::value;
template<typename From, typename To>
concept convertible_to = __is_convertible(From, To);
template<typename Derived, typename Base>
concept derived_from = __is_class(Derived) && __is_class(Base) && convertible_to<const volatile Derived*, const volatile Base*>;
template<typename Derived, typename Base>
concept same_or_derived_from = same_as<un_cvref_t<Derived>, Base> || derived_from<un_cvref_t<Derived>, Base>;
template<typename T> struct _tame_helper { using type = T; };
#define TAME_HELPER_IMPL(TRAILING) \
@ -194,6 +200,17 @@ template<typename R, typename... Args> struct _is_func_helper<R(Args...)> : true
template<typename T> concept is_func = _is_func_helper<tame_t<T>>::value;
template<typename T> struct _is_member_ptr_helper : false_type {};
template<typename C, typename T> struct _is_member_ptr_helper<T C::*> : true_type
{
static constexpr bool kIsFunc = is_func<T>;
};
template<typename T> concept is_member_ptr = _is_member_ptr_helper<un_cv_t<T>>::value;
template<typename T> concept is_member_func_ptr = is_member_ptr<T> && _is_member_ptr_helper<un_cv_t<T>>::kIsFunc;
template<typename T> concept is_member_data_ptr = is_member_ptr<T> && !_is_member_ptr_helper<un_cv_t<T>>::kIsFunc;
template<typename T> concept is_object = !is_void<T> && !is_ref<T> && !is_func<T>;
template<typename T> struct _array_helper : false_type { using type = T; };
@ -220,17 +237,75 @@ template<> struct _is_floating_point_helper<double> : true_type {};
template<typename T> concept is_floating_point = _is_floating_point_helper<un_cv_t<T>>::value;
template<typename T> struct _is_integer_helper : false_type {};
template<> struct _is_integer_helper<int8_t> : true_type {};
template<> struct _is_integer_helper<int16_t> : true_type {};
template<> struct _is_integer_helper<int32_t> : true_type {};
template<> struct _is_integer_helper<int64_t> : true_type {};
template<> struct _is_integer_helper<uint8_t> : true_type {};
template<> struct _is_integer_helper<uint16_t> : true_type {};
template<> struct _is_integer_helper<uint32_t> : true_type {};
template<> struct _is_integer_helper<uint64_t> : true_type {};
template<typename T> struct _integer_traits
{
static constexpr bool kSigned = false;
static constexpr bool kUnsigned = false;
};
template<typename T> concept is_integer = _is_integer_helper<un_cv_t<T>>::value;
template<> struct _integer_traits<uint8_t>
{
static constexpr bool kSigned = false;
static constexpr bool kUnsigned = true;
using as_signed = int8_t;
};
template<> struct _integer_traits<uint16_t>
{
static constexpr bool kSigned = false;
static constexpr bool kUnsigned = true;
using as_signed = int16_t;
};
template<> struct _integer_traits<uint32_t>
{
static constexpr bool kSigned = false;
static constexpr bool kUnsigned = true;
using as_signed = int32_t;
};
template<> struct _integer_traits<uint64_t>
{
static constexpr bool kSigned = false;
static constexpr bool kUnsigned = true;
using as_signed = int64_t;
};
template<> struct _integer_traits<int8_t>
{
static constexpr bool kSigned = true;
static constexpr bool kUnsigned = false;
using as_unsigned = uint8_t;
};
template<> struct _integer_traits<int16_t>
{
static constexpr bool kSigned = true;
static constexpr bool kUnsigned = false;
using as_unsigned = uint16_t;
};
template<> struct _integer_traits<int32_t>
{
static constexpr bool kSigned = true;
static constexpr bool kUnsigned = false;
using as_unsigned = uint32_t;
};
template<> struct _integer_traits<int64_t>
{
static constexpr bool kSigned = true;
static constexpr bool kUnsigned = false;
using as_unsigned = uint64_t;
};
template<typename T> concept is_signed_integer = _integer_traits<T>::kSigned;
template<typename T> concept is_unsigned_integer = _integer_traits<T>::kUnsigned;
template<typename T> concept is_integer = is_signed_integer<T> || is_unsigned_integer<T>;
template<is_signed_integer T> using as_unsigned_integer = _integer_traits<T>::as_unsigned;
template<is_unsigned_integer T> using as_signed_integer = _integer_traits<T>::as_signed;
template<typename T> concept is_enum = __is_enum(T);

View File

@ -135,6 +135,12 @@ static_assert(!asl::is_ref<void>);
static_assert(!asl::is_ref<void()>);
static_assert(!asl::is_ref<void() const &&>);
struct MyClass
{
int data;
int fn(int x) { return x; } // NOLINT
};
static_assert(asl::is_ptr<int*>);
static_assert(asl::is_ptr<const int* const>);
static_assert(asl::is_ptr<const volatile int*>);
@ -142,6 +148,28 @@ static_assert(!asl::is_ptr<int>);
static_assert(!asl::is_ptr<void>);
static_assert(!asl::is_ptr<void()>);
static_assert(!asl::is_ptr<void() const &&>);
static_assert(!asl::is_ptr<int MyClass::*>);
static_assert(!asl::is_ptr<int (MyClass::*)(int)>);
static_assert(!asl::is_member_ptr<int*>);
static_assert(!asl::is_member_ptr<void()>);
static_assert(!asl::is_member_ptr<void() const &&>);
static_assert(asl::is_member_ptr<int MyClass::*>);
static_assert(asl::is_member_ptr<int (MyClass::*)(int)>);
static_assert(!asl::is_member_data_ptr<int*>);
static_assert(!asl::is_member_data_ptr<void()>);
static_assert(!asl::is_member_data_ptr<void() const &&>);
static_assert(asl::is_member_data_ptr<int MyClass::*>);
static_assert(!asl::is_member_data_ptr<int (MyClass::*)(int)>);
static_assert(!asl::is_member_func_ptr<int*>);
static_assert(!asl::is_member_func_ptr<void()>);
static_assert(!asl::is_member_func_ptr<void() const &&>);
static_assert(!asl::is_member_func_ptr<int MyClass::*>);
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int)>);
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int) const>);
static_assert(asl::is_member_func_ptr<int (MyClass::*)(int) volatile &&>);
static_assert(asl::same_as<int, asl::tame_t<int>>);
static_assert(asl::same_as<int(), asl::tame_t<int()>>);
@ -224,6 +252,8 @@ static_assert(!asl::convertible_to<const int16_t(&)[], int16_t(&)[]>);
static_assert(!asl::convertible_to<D(&)[], C(&)[]>);
static_assert(asl::derived_from<Derived, Base>);
static_assert(asl::derived_from<Derived, Derived>);
static_assert(asl::derived_from<Base, Base>);
static_assert(!asl::derived_from<Base, Derived>);
static_assert(!asl::derived_from<D, C>);
static_assert(!asl::derived_from<C, D>);
@ -231,6 +261,16 @@ static_assert(!asl::derived_from<uint8_t, uint16_t>);
static_assert(!asl::derived_from<uint16_t, uint8_t>);
static_assert(!asl::derived_from<int, int>);
static_assert(asl::same_or_derived_from<Derived, Base>);
static_assert(asl::same_or_derived_from<Derived, Derived>);
static_assert(asl::same_or_derived_from<Base, Base>);
static_assert(!asl::same_or_derived_from<Base, Derived>);
static_assert(!asl::same_or_derived_from<D, C>);
static_assert(!asl::same_or_derived_from<C, D>);
static_assert(!asl::same_or_derived_from<uint8_t, uint16_t>);
static_assert(!asl::same_or_derived_from<uint16_t, uint8_t>);
static_assert(asl::same_or_derived_from<int, int>);
static_assert(!asl::is_const<int>);
static_assert(asl::is_const<const int>);
static_assert(!asl::is_const<const int*>);

43
asl/base/numeric.hpp Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2025 Steven Le Rouzic
//
// SPDX-License-Identifier: BSD-3-Clause
#pragma once
#include "asl/base/integers.hpp"
#include "asl/base/bit.hpp"
#include "asl/base/meta.hpp"
namespace asl
{
template<is_integer T>
constexpr bool is_pow2(T x)
{
using unsigned_type = select_t<is_unsigned_integer<T>, T, as_unsigned_integer<T>>;
return x > 0 && has_single_bit(static_cast<unsigned_type>(x));
}
template<typename T>
concept is_numeric = is_integer<T> || is_floating_point<T>;
template<is_numeric T>
constexpr T min(T a, T b)
{
return (a <= b) ? a : b;
}
template<is_numeric T>
constexpr T max(T a, T b)
{
return (a >= b) ? a : b;
}
template<is_numeric T>
constexpr T clamp(T x, T a, T b)
{
return min(max(x, a), b);
}
} // namespace asl

View File

@ -0,0 +1,16 @@
// Copyright 2025 Steven Le Rouzic
//
// SPDX-License-Identifier: BSD-3-Clause
#include "asl/base/numeric.hpp"
#include "asl/testing/testing.hpp"
ASL_TEST(is_pow2)
{
ASL_TEST_EXPECT(asl::is_pow2(4));
ASL_TEST_EXPECT(asl::is_pow2(65536));
ASL_TEST_EXPECT(!asl::is_pow2(6));
ASL_TEST_EXPECT(!asl::is_pow2(1978));
ASL_TEST_EXPECT(!asl::is_pow2(0));
}

View File

@ -47,9 +47,6 @@ template<typename T>
namespace asl
{
struct in_place_t {};
static constexpr in_place_t in_place{};
template<moveable T>
constexpr void swap(T& a, T& b)
{
@ -72,39 +69,6 @@ constexpr U bit_cast(T value) requires (sizeof(T) == sizeof(U))
return __builtin_bit_cast(U, value);
}
template<typename T>
constexpr T min(T a, T b)
{
return (a <= b) ? a : b;
}
template<typename T>
constexpr T max(T a, T b)
{
return (a >= b) ? a : b;
}
constexpr uint64_t round_up_pow2(uint64_t v)
{
ASL_ASSERT(v <= 0x8000'0000'0000'0000);
v -= 1;
v |= v >> 1U;
v |= v >> 2U;
v |= v >> 4U;
v |= v >> 8U;
v |= v >> 16U;
v |= v >> 32U;
return v + 1;
}
constexpr bool is_pow2(isize_t v)
{
return v > 0 && ((v - 1) & v) == 0; // NOLINT
}
// NOLINTBEGIN(*-macro-parentheses)
#define ASL_DELETE_COPY(T) \
T(const T&) = delete; \

View File

@ -13,6 +13,7 @@ cc_library(
],
deps = [
"//asl/memory",
"//asl/memory:allocator",
"//asl/base",
"//asl/types:span",
"//asl/hashing",
@ -28,6 +29,7 @@ cc_library(
deps = [
"//asl/base",
"//asl/memory",
"//asl/memory:allocator",
"//asl/types:maybe_uninit",
"//asl/hashing",
],
@ -42,6 +44,7 @@ cc_library(
deps = [
"//asl/base",
"//asl/memory",
"//asl/memory:allocator",
"//asl/hashing",
":hash_set",
],

View File

@ -9,6 +9,7 @@
#include "asl/memory/memory.hpp"
#include "asl/base/annotations.hpp"
#include "asl/base/assert.hpp"
#include "asl/base/bit.hpp"
#include "asl/types/span.hpp"
#include "asl/hashing/hash.hpp"
@ -320,7 +321,7 @@ public:
if (new_capacity <= capacity()) { return; }
ASL_ASSERT(new_capacity > kInlineCapacity);
new_capacity = static_cast<isize_t>(round_up_pow2(static_cast<uint64_t>(new_capacity)));
new_capacity = static_cast<isize_t>(bit_ceil(static_cast<uint64_t>(new_capacity)));
T* old_data = data();
const isize_t old_capacity = capacity();

View File

@ -7,6 +7,8 @@
#include "asl/base/annotations.hpp"
#include "asl/base/utility.hpp"
#include "asl/base/meta.hpp"
#include "asl/base/bit.hpp"
#include "asl/base/numeric.hpp"
#include "asl/memory/allocator.hpp"
#include "asl/memory/memory.hpp"
#include "asl/types/maybe_uninit.hpp"
@ -83,7 +85,7 @@ protected:
ASL_ASSERT(size > 0);
return max<isize_t>(
kMinCapacity,
static_cast<isize_t>(round_up_pow2((static_cast<uint64_t>(size) * 4 + 2) / 3)));
static_cast<isize_t>(bit_ceil((static_cast<uint64_t>(size) * 4 + 2) / 3)));
}
static void insert_inner(

View File

@ -4,6 +4,7 @@
#include "asl/formatting/format.hpp"
#include "asl/base/float.hpp"
#include "asl/base/numeric.hpp"
#define JKJ_STD_REPLACEMENT_NAMESPACE_DEFINED 0
#define JKJ_STATIC_DATA_SECTION_DEFINED 0

View File

@ -9,16 +9,27 @@ package(
cc_library(
name = "memory",
hdrs = [
"allocator.hpp",
"layout.hpp",
"memory.hpp",
],
srcs = [
"allocator.cpp",
],
deps = [
"//asl/base",
],
visibility = ["//visibility:public"],
)
cc_library(
name = "allocator",
hdrs = [
"allocator.hpp",
],
srcs = [
"allocator.cpp",
],
deps = [
"//asl/base",
"//asl/memory",
],
visibility = ["//visibility:public"],
)

View File

@ -17,6 +17,17 @@ constexpr void* operator new(size_t, void* ptr) noexcept
namespace asl
{
template<typename T>
[[nodiscard]]
constexpr T* address_of(T& obj)
{
return __builtin_addressof(obj);
}
template<typename T>
void address_of(const T&& obj) = delete;
[[nodiscard]]
constexpr isize_t memcmp(const void* a, const void* b, isize_t size)
{
return __builtin_memcmp(a, b, static_cast<size_t>(size));

View File

@ -14,6 +14,7 @@ cc_library(
deps = [
"//asl/base",
"//asl/memory",
"//asl/memory:allocator",
"//asl/hashing",
],
visibility = ["//visibility:public"],
@ -86,6 +87,7 @@ cc_library(
deps = [
"//asl/base",
"//asl/memory",
"//asl/memory:allocator",
],
visibility = ["//visibility:public"],
)
@ -97,6 +99,7 @@ cc_library(
],
deps = [
"//asl/base",
"//asl/memory",
],
visibility = ["//visibility:public"],
)

View File

@ -7,6 +7,7 @@
#include "asl/base/utility.hpp"
#include "asl/base/meta.hpp"
#include "asl/base/functional.hpp"
#include "asl/memory/memory.hpp"
namespace asl
{
@ -43,7 +44,7 @@ public:
&& same_as<invoke_result_t<T, Args...>, R>
)
// NOLINTNEXTLINE(*cast*)
: m_obj{const_cast<void*>(reinterpret_cast<const void*>(&t))}
: m_obj{const_cast<void*>(reinterpret_cast<const void*>(address_of(t)))}
, m_invoke{invoke<un_ref_t<T>>}
{}
@ -56,7 +57,7 @@ public:
)
{
// NOLINTNEXTLINE(*cast*)
m_obj = const_cast<void*>(reinterpret_cast<const void*>(&t));
m_obj = const_cast<void*>(reinterpret_cast<const void*>(address_of(t)));
m_invoke = invoke<un_ref_t<T>>;
return *this;