based

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

commit ef3f2834e6555b3b4dd23a38cba3bf3151d24862
parent 4752fae51bff488042fb30482b6e2f0bcc1bba50
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Fri, 2 May 2025 21:04:10 +0200

Fix for_each and reduce, callable needs more work

Diffstat:
M example/type_traits.cpp | ++ -----------
M include/based/algorithm.hpp | ++++++ -----------
M include/based/type_traits.hpp | ++++++++++++++++++++++++++++++++ -----------
M test/source/callable_test.cpp | ++++++++++++++++++ ----------
M test/source/reduce_test.cpp | ++++++++++ --

5 files changed, 68 insertions(+), 45 deletions(-)


diff --git a/ example/type_traits.cpp b/ example/type_traits.cpp

@@ -16,11 +16,6 @@ struct irregular

~irregular() = default;
};

struct no_return
{
void operator()() {}
};

template<typename T>
struct identity
{

@@ -49,10 +44,6 @@ T sub(T val1, U val2)


int main()
{
static_assert(based::Procedure<no_return, void>);
static_assert(!based::RegularProcedure<no_return, void>);
static_assert(!based::FunctionalProcedure<no_return, void>);

using id = identity<double>;
using ii = identity<irregular>;

@@ -71,8 +62,8 @@ int main()

using adi = add<double, irregular>;

static_assert(based::Procedure<ad, double, double, double>);
static_assert(based::Procedure<ai, irregular, irregular, irregular>);
static_assert(based::Procedure<aid, irregular, irregular, double>);
static_assert(!based::Procedure<ai, irregular, irregular, irregular>);
static_assert(!based::Procedure<aid, irregular, irregular, double>);
static_assert(based::Procedure<adi, double, double, irregular>);

static_assert(based::RegularProcedure<ad, double, double, double>);

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

@@ -163,8 +163,7 @@ 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, Callable Proc>
requires IterUnaryProcedure<Proc, ret_t<Proc>, I>
template<ReadableIterator I, IterUnaryProcedure<void, I> Proc>
Proc for_each(I first, I last, Proc proc)
{
// Precondition: readable_bounded_range(first, last);

@@ -323,8 +322,7 @@ 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<Iterator I, Callable F, Callable Op>
requires(UnaryFunction<F, ret_t<F>, I> && BinaryOperation<Op, ret_t<F>>)
template<Iterator I, UnaryFunction<void, I> F, BinaryOperation<ret_t<F, I>> Op>
auto reduce_nonempty(I first, I last, Op opr, F fun)
{
assert(first != last);

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

return res;
}

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

@@ -358,8 +355,7 @@ auto reduce(

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

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

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

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

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


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

template<ReadableIterator I, Callable Proc>
requires IterUnaryProcedure<Proc, ret_t<Proc>, I>
template<ReadableIterator I, IterUnaryProcedure<void, I> Proc>
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

@@ -440,7 +440,10 @@ function(F) -> function<Sig>;


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

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

template<typename T>
struct callable;

template<typename T>

@@ -465,10 +468,10 @@ template<typename T>

concept Callable = is_instantiable_v<callable, T>;

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

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

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

@@ -494,23 +497,41 @@ template<typename... Args>

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

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

namespace detail
{

// clang-format off

template<typename P, typename Sig> struct procedure : public false_type {};

template<typename P, typename Ret, typename... Args>
concept Procedure = requires {
requires(Invocable<P, Args...>);
requires(SameAs<void, Ret> || SameAs<Ret, std::invoke_result_t<P, Args...>>);
};
requires (Invocable<P, Args...> && std::convertible_to<std::invoke_result_t<P, Args...>, Ret>)
struct procedure<P, Ret(Args...)> : public true_type {};

template<typename P, typename... Args>
requires (Invocable<P, Args...>)
struct procedure<P, void(Args...)> : public true_type {};

template<typename P, typename Ret, typename... Args>
static constexpr bool procedure_v = procedure<P, Ret(Args...)>::value;

// clang-format on

} // namespace detail

template<typename P, typename Ret, typename... Args>
concept Procedure = detail::procedure_v<P, Ret, Args...>;

template<typename P, typename Ret, typename Arg>
concept UnaryProcedure = Procedure<P, Ret, Arg>;

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

template<typename P, typename Ret, typename... Args>

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

@@ -4,9 +4,11 @@


#include "based/type_traits.hpp"


namespace
{

// NOLINTNEXTLINE(*need*)
int free_func(int a, double b)
{
return static_cast<int>(a + b);

@@ -21,8 +23,8 @@ 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>);
STATIC_REQUIRE(SameAs<based::callable_sig_t<type_t>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::callable_ret_t<type_t>, int>);
}

TEST_CASE("lambda", "[type_traits/callable]")

@@ -34,18 +36,24 @@ TEST_CASE("lambda", "[type_traits/callable]")

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>);
STATIC_REQUIRE(SameAs<based::callable_sig_t<type_t>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::callable_ret_t<type_t>, int>);
}

/*
struct func
{
auto operator()(auto a, auto b) { return static_cast<int>(a + b); }
};

TEST_CASE("member function", "[type_traits/callable]")
{
struct func
{
int operator()(int a, double b) { return static_cast<int>(a + b); }
};
// [&](auto&&... args) -> decltype(auto) { return
// f(std::forward<decltype(args)>(args)...); }

// based::error_template<decltype(&func::template operator()<int, double>)>();
STATIC_REQUIRE(based::Callable<func>);
STATIC_REQUIRE(SameAs<based::sig_t<func>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::ret_t<func>, int>);
STATIC_REQUIRE(SameAs<based::callable_sig_t<func>, int(int, double)>);
STATIC_REQUIRE(SameAs<based::callable_ret_t<func>, int>);
}
*/

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

@@ -15,7 +15,16 @@ struct fun

auto operator()(based::Iterator auto itr) { return *itr; }
};

/*
TEST_CASE("reduce_nonempty(sequence)", "[algorithm/reduce_nonempty]")
{
const std::array arr = {0, 1, 2, 3, 4, 5};

const auto count =
based::reduce_nonempty(std::cbegin(arr), std::cend(arr), op {}, fun {});

REQUIRE(count == 15);
}

TEST_CASE("reduce(empty)", "[algorithm/reduce]")
{
const std::array<int, 0> arr = {};

@@ -55,4 +64,3 @@ TEST_CASE("reduce_nonzero(sequence)", "[algorithm/reduce_nonzero]")


REQUIRE(count == 15);
}
*/