basedOpinionated 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...
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>);
}