based

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

commit 4752fae51bff488042fb30482b6e2f0bcc1bba50
parent 405cd0e9db6d67a8f3011662610cc528cd0ebc7c
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Fri, 2 May 2025 10:33:51 +0200

Step towards funciton unification, progress...

Diffstat:
M include/based/algorithm.hpp | +++++++++++ ------------------
M include/based/type_traits.hpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --
M test/CMakeLists.txt | +
A test/source/callable_test.cpp | +++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 117 insertions(+), 20 deletions(-)


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

@@ -163,7 +163,8 @@ std::pair<I, I> minmax_element(I first, I last)

return based::minmax_element(first, last, std::less<iter_value_t<I>>());
}

template<ReadableIterator I, IterUnaryProcedure<void, I> Proc>
template<ReadableIterator I, Callable Proc>
requires IterUnaryProcedure<Proc, ret_t<Proc>, I>
Proc for_each(I first, I last, Proc proc)
{
// Precondition: readable_bounded_range(first, last);

@@ -322,11 +323,8 @@ iter_dist_t<I> count_if_not(I first, I last, Pred pred)

return count_if_not(first, last, pred, iter_dist_t<I> {0});
}

template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
template<Iterator I, Callable F, Callable Op>
requires(UnaryFunction<F, ret_t<F>, I> && BinaryOperation<Op, ret_t<F>>)
auto reduce_nonempty(I first, I last, Op opr, F fun)
{
assert(first != last);

@@ -342,11 +340,8 @@ auto reduce_nonempty(I first, I last, Op opr, F fun)

return res;
}

template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
template<Iterator I, Callable F, Callable Op>
requires(UnaryFunction<F, ret_t<F>, I> && BinaryOperation<Op, ret_t<F>>)
auto reduce(
I first,
I last,

@@ -363,11 +358,8 @@ auto reduce(

return reduce_nonempty(first, last, opr, fun);
}

template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
template<Iterator I, Callable F, Callable Op>
requires(UnaryFunction<F, ret_t<F>, I> && BinaryOperation<Op, ret_t<F>>)
auto reduce_nonzero(
I first,
I last,

@@ -378,7 +370,7 @@ auto reduce_nonzero(

{
// Precondition: bounded_range(first, last)
// Precondition: partially_associative(opr)
Ret res;
ret_t<F> res;
do {
if (first == last) {
return zero;

@@ -457,7 +449,8 @@ bool increasing_range(I first, I last, Rel rel)


/* ----- Counted Range Algorithms ----- */

template<ReadableIterator I, IterUnaryProcedure<void, I> Proc>
template<ReadableIterator I, Callable Proc>
requires IterUnaryProcedure<Proc, ret_t<Proc>, I>
auto for_each_n(I first, iter_dist_t<I> size, Proc proc)
{
// Precondition: readable_weak_range(first, size);

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

@@ -66,6 +66,16 @@ template<class T> struct remove_pointer<T* volatile> { using type = T; };

template<class T> struct remove_pointer<T* const volatile> { using type = T; };
template<class T> using remove_pointer_t = typename remove_pointer<T>::type;


template<template<class...> class T, class Void, class... Ts>
struct is_instantiable : false_type {};

template<template<class...> class T, class... Ts>
struct is_instantiable<T, void_t<T<Ts...>>, Ts...> : true_type {};

template<template<class...> class T, class... Ts>
inline constexpr auto is_instantiable_v = is_instantiable<T, void, Ts...>::value;

// clang-format on

/* ----- Integer ----- */

@@ -90,6 +100,9 @@ template<typename T>

using bare_t = remove_cvref_t<T>;

template<typename T>
concept Semiregular = std::semiregular<T>;

template<typename T>
concept Regular = std::regular<T>;

template<typename T>

@@ -425,6 +438,38 @@ template<typename F, typename Sig = signature_t<F, decltype(&F::operator())>>

function(F) -> function<Sig>;
*/

/* ----- Callable Interface ----- */

template<typename Sig>
struct callable;

template<typename T>
requires(std::is_function_v<T>)
struct callable<T> : public signature<std::decay_t<T>>
{
};

template<typename T>
requires(requires { &std::decay_t<T>::operator(); })
struct callable<T> : public signature<decltype(&T::operator())>
{
};

template<typename T>
requires(std::is_member_function_pointer_v<std::decay_t<T>>)
struct callable<T> : public signature<remove_pointer_t<T>>
{
};

template<typename T>
concept Callable = is_instantiable_v<callable, T>;

template<Callable T>
using sig_t = typename callable<T>::signature::sig_type;

template<Callable T>
using ret_t = typename callable<T>::signature::ret_type;

/* ----- Function Concepts ----- */

template<typename T>

@@ -437,6 +482,9 @@ template<std::size_t idx, typename... Args>

using elem_t = std::tuple_element_t<idx, std::tuple<Args...>>;

template<typename... Args>
concept SemiregularDomain = (Semiregular<remove_cvref_t<Args>> && ...);

template<typename... Args>
concept RegularDomain = (Regular<remove_cvref_t<Args>> && ...);

template<typename... Args>

@@ -445,9 +493,12 @@ concept InputDomain = (Input<Args> && ...);

template<typename... Args>
concept HomogeneousDomain = (SameAs<elem_t<0, Args...>, Args> && ...);

template<typename P, typename... Args>
concept Invocable = std::invocable<P, Args...>;

template<typename P, typename Ret, typename... Args>
concept Procedure = requires {
requires(std::invocable<P, Args...>);
requires(Invocable<P, Args...>);
requires(SameAs<void, Ret> || SameAs<Ret, std::invoke_result_t<P, Args...>>);
};

@@ -456,7 +507,8 @@ concept UnaryProcedure = Procedure<P, Ret, Arg>;


template<typename P, typename Ret, typename... Args>
concept RegularProcedure = requires {
requires(Procedure<P, Ret, Args...>);
requires(Procedure<P, void, Args...>);
requires(SameAs<void, Ret> || std::convertible_to<std::invoke_result_t<P, Args...>, Ret>);
requires(RegularDomain<Args...>);
requires(Regular<Ret>);
};

diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt

@@ -26,6 +26,7 @@ endfunction()


add_test(standard_traits_test)
add_test(signature_test)
add_test(callable_test)

## ----- Min and Max -----

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

@@ -0,0 +1,51 @@

// #define CATCH_CONFIG_RUNTIME_STATIC_REQUIRE

#include <catch2/catch_test_macros.hpp>

#include "based/type_traits.hpp"

namespace
{

int free_func(int a, double b)
{
return static_cast<int>(a + b);
}

} // namespace

using based::SameAs;

TEST_CASE("free function", "[type_traits/callable]")
{
using type_t = decltype(free_func);

STATIC_REQUIRE(based::Callable<type_t>);
STATIC_REQUIRE(SameAs<based::sig_t<type_t>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::ret_t<type_t>, int>);
}

TEST_CASE("lambda", "[type_traits/callable]")
{
const auto func = [](int a, double b)
{
return static_cast<int>(a + b);
};
using type_t = decltype(func);

STATIC_REQUIRE(based::Callable<type_t>);
STATIC_REQUIRE(SameAs<based::sig_t<type_t>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::ret_t<type_t>, int>);
}

TEST_CASE("member function", "[type_traits/callable]")
{
struct func
{
int operator()(int a, double b) { return static_cast<int>(a + b); }
};

STATIC_REQUIRE(based::Callable<func>);
STATIC_REQUIRE(SameAs<based::sig_t<func>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::ret_t<func>, int>);
}