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