basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | c4faa23f38f69f7100c2444637aed7f312db5704 |
parent | 523b08ac6fe91c3418e5613ccaee506a7c4aa781 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Wed, 30 Apr 2025 13:39:52 +0200 |
Rework function concepts to enforce return type
- reduce doesn't work
M | CMakeLists.txt | | | + - |
M | example/type_traits.cpp | | | ++++++++++++++++++++++++++++++++ ------------------------------------- |
M | include/based/algorithm.hpp | | | +++++++++++++++++++ ----------- |
M | include/based/type_traits.hpp | | | +++++++++++++++++++++++++++++++ --------------------------------------------------- |
M | test/source/reduce_test.cpp | | | ++ |
5 files changed, 94 insertions(+), 115 deletions(-)
diff --git a/ CMakeLists.txt b/ CMakeLists.txt
@@ -4,7 +4,7 @@
include(cmake/prelude.cmake)
project(
based
VERSION 0.1.0
VERSION 0.1.1
DESCRIPTION "Opinionated utility library"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/based.git"
LANGUAGES CXX
diff --git a/ example/type_traits.cpp b/ example/type_traits.cpp
@@ -49,24 +49,20 @@
T sub(T val1, U val2)
int main()
{
static_assert(based::arity_v<no_return> == 0);
static_assert(based::Procedure<no_return>);
static_assert(!based::RegularProcedure<no_return>);
static_assert(!based::FunctionalProcedure<no_return>);
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>;
static_assert(std::same_as<based::codomain_t<id, double>, double>);
static_assert(based::arity_v<id, double> == 1);
static_assert(based::Procedure<id, double>);
static_assert(based::Procedure<id, double, double>);
static_assert(!based::Procedure<ii, irregular>);
static_assert(based::RegularProcedure<id, double>);
static_assert(based::RegularProcedure<id, double, double>);
static_assert(!based::RegularProcedure<ii, irregular>);
static_assert(based::FunctionalProcedure<id, double>);
static_assert(based::FunctionalProcedure<id, double, double>);
static_assert(!based::FunctionalProcedure<ii, irregular>);
using ad = add<double, double>;
@@ -74,58 +70,57 @@
int main()
using aid = add<irregular, double>;
using adi = add<double, irregular>;
static_assert(std::same_as<based::codomain_t<ad, double, double>, double>);
static_assert(based::arity_v<ad, double, double> == 2);
static_assert(based::Procedure<ad, double, double>);
static_assert(based::Procedure<ai, irregular, irregular>);
static_assert(based::Procedure<aid, irregular, double>);
static_assert(based::Procedure<adi, 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<adi, double, double, irregular>);
static_assert(based::RegularProcedure<ad, double, double>);
static_assert(!based::RegularProcedure<ai, irregular, irregular>);
static_assert(!based::RegularProcedure<aid, irregular, double>);
static_assert(!based::RegularProcedure<adi, double, irregular>);
static_assert(based::RegularProcedure<ad, double, double, double>);
static_assert(!based::RegularProcedure<ai, irregular, irregular, irregular>);
static_assert(!based::RegularProcedure<aid, irregular, irregular, double>);
static_assert(!based::RegularProcedure<adi, double, double, irregular>);
static_assert(based::FunctionalProcedure<ad, double, double>);
static_assert(!based::FunctionalProcedure<ai, irregular, irregular>);
static_assert(!based::FunctionalProcedure<aid, irregular, double>);
static_assert(!based::FunctionalProcedure<adi, double, irregular>);
static_assert(based::FunctionalProcedure<ad, double, double, double>);
static_assert(!based::
FunctionalProcedure<ai, irregular, irregular, irregular>);
static_assert(!based::FunctionalProcedure<aid, irregular, irregular, double>);
static_assert(!based::FunctionalProcedure<adi, double, double, irregular>);
using md = mutate<double>;
static_assert(based::Procedure<md, double*>);
static_assert(based::RegularProcedure<md, double*>);
static_assert(!based::FunctionalProcedure<md>);
static_assert(based::Procedure<md, double, double*>);
static_assert(based::RegularProcedure<md, double, double*>);
static_assert(!based::FunctionalProcedure<md, double, double*>);
static_assert(based::RegularProcedure<
decltype(sub<double, double>),
double,
double,
double>);
static const auto func1 = [](double /* a */)
{
return 1;
};
static_assert(based::Procedure<decltype(func1), double>);
static_assert(based::RegularProcedure<decltype(func1), double>);
static_assert(based::FunctionalProcedure<decltype(func1), double>);
static_assert(based::Procedure<decltype(func1), int, double>);
static_assert(based::RegularProcedure<decltype(func1), int, double>);
static_assert(based::FunctionalProcedure<decltype(func1), int, double>);
static const auto func2 = [](irregular /* a */)
{
return 1;
};
static_assert(!based::Procedure<decltype(func2), irregular>);
static_assert(!based::RegularProcedure<decltype(func2), irregular>);
static_assert(!based::FunctionalProcedure<decltype(func2), irregular>);
static_assert(!based::Procedure<decltype(func2), int, irregular>);
static_assert(!based::RegularProcedure<decltype(func2), int, irregular>);
static_assert(!based::FunctionalProcedure<decltype(func2), int, irregular>);
static const auto func3 = [](const irregular& /* a */)
{
return 1;
};
static_assert(based::Procedure<decltype(func3), irregular>);
static_assert(!based::RegularProcedure<decltype(func3), irregular>);
static_assert(!based::FunctionalProcedure<decltype(func3), irregular>);
static_assert(based::Procedure<decltype(func3), int, irregular>);
static_assert(!based::RegularProcedure<decltype(func3), int, irregular>);
static_assert(!based::FunctionalProcedure<decltype(func3), int, irregular>);
return 0;
}
diff --git a/ include/based/algorithm.hpp b/ include/based/algorithm.hpp
@@ -16,11 +16,7 @@
namespace detail
/* ----- Min and Max ----- */
template<typename P, typename Arg>
concept NoninputRelation = requires {
requires(RegularProcedure<P, Arg, Arg>);
requires(std::same_as<bool, codomain_t<P, Arg, Arg>>);
requires(arity_v<P, Arg, Arg> == 2);
};
concept NoninputRelation = RegularProcedure<P, bool, Arg, Arg>;
} // namespace detail
@@ -167,7 +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, IterUnaryProcedure<I> Proc>
template<ReadableIterator I, IterUnaryProcedure<void, I> Proc>
Proc for_each(I first, I last, Proc proc)
{
// Precondition: readable_bounded_range(first, last);
@@ -326,7 +322,11 @@
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, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
auto reduce_nonempty(I first, I last, Op opr, F fun)
{
assert(first != last);
@@ -342,7 +342,11 @@
auto reduce_nonempty(I first, I last, Op opr, F fun)
return res;
}
template<Iterator I, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
auto reduce(
I first,
I last,
@@ -359,7 +363,11 @@
auto reduce(
return reduce_nonempty(first, last, opr, fun);
}
template<Iterator I, UnaryFunction<I> F, BinaryOperation<codomain_t<F, I>> Op>
template<
typename Ret,
Iterator I,
UnaryFunction<Ret, I> F,
BinaryOperation<Ret> Op>
auto reduce_nonzero(
I first,
I last,
@@ -370,7 +378,7 @@
auto reduce_nonzero(
{
// Precondition: bounded_range(first, last)
// Precondition: partially_associative(opr)
codomain_t<F, I> res;
Ret res;
do {
if (first == last) {
return zero;
@@ -449,7 +457,7 @@
bool increasing_range(I first, I last, Rel rel)
/* ----- Counted Range Algorithms ----- */
template<ReadableIterator I, IterUnaryProcedure<I> Proc>
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
@@ -139,6 +139,10 @@
struct is_homogeneous_domain<Head, Args...> : std::true_type
} // namespace detail
template<std::size_t idx, typename... Args>
requires(idx < sizeof...(Args))
using elem_t = std::tuple_element_t<idx, std::tuple<Args...>>;
template<typename... Args>
concept RegularDomain =
requires { requires(Regular<std::remove_cvref_t<Args>> && ...); };
@@ -147,121 +151,91 @@
template<typename... Args>
concept InputDomain = requires { requires(Input<Args> && ...); };
template<typename... Args>
concept HomogenousDomain = detail::is_homogeneous_domain<Args...>::value;
concept HomogeneousDomain = detail::is_homogeneous_domain<Args...>::value;
template<typename T>
using distance_t = std::uint64_t;
template<typename P, typename... Args>
concept Procedure = std::invocable<P, Args...>;
template<typename P, typename... Args>
requires Procedure<P, Args...>
inline constexpr auto arity_v = std::tuple_size<std::tuple<Args...>>::value;
template<typename P, std::size_t idx, typename... Args>
requires Procedure<P, Args...> && requires { idx < arity_v<Args...>; }
using domain_elem_t =
std::decay_t<std::tuple_element_t<idx, std::tuple<Args...>>>;
template<typename P, typename... Args>
requires Procedure<P, Args...>
using codomain_t = std::invoke_result_t<P, Args...>;
template<typename P, typename Arg>
concept UnaryProcedure = requires {
requires(Procedure<P, Arg>);
requires(arity_v<P, Arg> == 1);
template<typename P, typename Ret, typename... Args>
concept Procedure = requires {
requires(std::invocable<P, Args...>);
requires(std::same_as<void, Ret> || std::same_as<Ret, std::invoke_result_t<P, Args...>>);
};
template<typename P, typename... 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, Args...>);
requires(Procedure<P, Ret, Args...>);
requires(RegularDomain<Args...>);
requires(Regular<codomain_t<P, Args...>>);
requires(Regular<Ret>);
};
template<typename P, typename... Args>
template<typename P, typename Ret, typename... Args>
concept FunctionalProcedure = requires {
requires(RegularProcedure<P, Args...>);
requires(RegularProcedure<P, Ret, Args...>);
requires(InputDomain<Args...>);
};
template<typename P, typename Arg>
template<typename P, typename Ret, typename Arg>
concept UnaryFunction = requires {
requires(FunctionalProcedure<P, Arg>);
requires(UnaryProcedure<P, Arg>);
requires(FunctionalProcedure<P, Ret, Arg>);
requires(UnaryProcedure<P, Ret, Arg>);
};
template<typename P, typename... Args>
template<typename P, typename Ret, typename... Args>
concept HomogeneousFunction = requires {
requires(FunctionalProcedure<P, Args...>);
requires(arity_v<P, Args...> > 0);
requires(HomogenousDomain<Args...>);
requires(FunctionalProcedure<P, Ret, Args...>);
requires(sizeof...(Args) > 0);
requires(HomogeneousDomain<Args...>);
};
template<typename P, typename... Args>
concept Predicate = requires {
requires(FunctionalProcedure<P, Args...>);
requires(std::same_as<bool, codomain_t<P, Args...>>);
};
concept Predicate = FunctionalProcedure<P, bool, Args...>;
template<typename P, typename... Args>
concept HomogeneousPredicate = requires {
requires(Predicate<P, Args...>);
requires(HomogeneousFunction<P, Args...>);
requires(HomogeneousFunction<P, bool, Args...>);
};
template<typename P, typename Arg>
concept UnaryPredicate = requires {
requires(Predicate<P, Arg>);
requires(UnaryFunction<P, Arg>);
requires(UnaryFunction<P, bool, Arg>);
};
template<typename P, typename... Args>
concept Operation = requires {
requires(HomogeneousFunction<P, Args...>);
requires(BareSameAs<
codomain_t<P, Args...>,
std::tuple_element_t<0, std::tuple<Args...>>>);
};
concept Operation = HomogeneousFunction<P, elem_t<0, Args...>, Args...>;
template<typename P, typename Arg>
template<typename P, typename Ret, typename Arg>
concept Transformation = requires {
requires(Operation<P, Arg>);
requires(UnaryFunction<P, Arg>);
requires(Operation<P, Ret, Arg>);
requires(UnaryFunction<P, Ret, Arg>);
};
template<typename P, typename Arg>
concept BinaryOperation = requires {
requires(Operation<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
concept BinaryOperation = Operation<P, Arg, Arg>;
template<typename P, typename Arg>
concept AssociativeBinaryOperation = requires {
requires(Operation<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
concept AssociativeBinaryOperation = Operation<P, Arg, Arg>;
template<typename P, typename Arg>
concept Relation = requires {
requires(HomogeneousPredicate<P, Arg, Arg>);
requires(arity_v<P, Arg, Arg> == 2);
};
concept Relation = HomogeneousPredicate<P, Arg, Arg>;
/* ----- Iterator variants ----- */
template<typename P, typename I>
template<typename P, typename Ret, typename I>
concept IterUnaryProcedure = requires {
requires(Iterator<I>);
requires(UnaryProcedure<P, iter_value_t<I>>);
requires(UnaryProcedure<P, Ret, iter_value_t<I>>);
};
template<typename P, typename I>
template<typename P, typename Ret, typename I>
concept IterUnaryFunction = requires {
requires(Iterator<I>);
requires(UnaryFunction<P, iter_value_t<I>>);
requires(UnaryFunction<P, Ret, iter_value_t<I>>);
};
template<typename P, typename... I>
@@ -276,13 +250,13 @@
concept IterUnaryPredicate = requires {
requires(UnaryPredicate<P, iter_value_t<I>>);
};
template<typename P, typename... I>
template<typename P, typename Ret, typename... I>
concept IterOperation = requires {
requires(Iterator<I> && ...);
requires(Operation<P, iter_value_t<I>...>);
};
template<typename P, typename I>
template<typename P, typename Ret, typename I>
concept IterBinaryOperation = requires {
requires(Iterator<I>);
requires(BinaryOperation<P, iter_value_t<I>>);
diff --git a/ test/source/reduce_test.cpp b/ test/source/reduce_test.cpp
@@ -15,6 +15,7 @@
struct fun
auto operator()(based::Iterator auto itr) { return *itr; }
};
/*
TEST_CASE("reduce(empty)", "[algorithm/reduce]")
{
const std::array<int, 0> arr = {};
@@ -54,3 +55,4 @@
TEST_CASE("reduce_nonzero(sequence)", "[algorithm/reduce_nonzero]")
REQUIRE(count == 15);
}
*/