This commit is contained in:
2017-10-23 03:43:50 +07:00
commit a519c925c8
6 changed files with 12800 additions and 0 deletions

8
CMakeLists.txt Normal file
View File

@@ -0,0 +1,8 @@
cmake_minimum_required(VERSION 3.5)
project(kari)
file(GLOB kari_src "*.cpp" "*.hpp")
add_executable(${PROJECT_NAME} ${kari_src})
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO)

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Matvey Cherevko
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1
README.md Normal file
View File

@@ -0,0 +1 @@
# Experimental library for currying in C++14.

11606
catch.hpp Normal file

File diff suppressed because it is too large Load Diff

507
kari.hpp Normal file
View File

@@ -0,0 +1,507 @@
#pragma once
#include <tuple>
#include <limits>
#include <utility>
#include <functional>
#include <type_traits>
namespace kari
{
namespace detail
{
namespace std_ext
{
//
// void_t
//
template < typename... Ts >
struct make_void {
using type = void;
};
template < typename... Ts >
using void_t = typename make_void<Ts...>::type;
//
// apply
//
template < typename F, typename Tuple, std::size_t... I >
constexpr decltype(auto) apply_impl(F&& f, Tuple&& args, std::index_sequence<I...>) {
return std::forward<F>(f)(std::get<I>(std::forward<Tuple>(args))...);
}
template < typename F, typename Tuple >
constexpr decltype(auto) apply(F&& f, Tuple&& args) {
return apply_impl(
std::forward<F>(f),
std::forward<Tuple>(args),
std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
//
// is_invocable, is_invocable_v
//
namespace detail
{
template < typename F, typename = void >
struct is_invocable_impl
: std::false_type {};
template < typename F, typename... Args >
struct is_invocable_impl<
F(Args...),
void_t<decltype(std::declval<F>()(std::declval<Args>()...))>
> : std::true_type {};
}
template < typename F, typename... Args >
struct is_invocable
: detail::is_invocable_impl<F(Args...)> {};
template < typename F, typename... Args >
constexpr bool is_invocable_v = is_invocable<F, Args...>::value;
//
// conjunction, conjunction_v
//
template < typename... >
struct conjunction
: std::true_type {};
template < typename B >
struct conjunction<B>
: B {};
template < typename B, typename... Bs>
struct conjunction<B, Bs...>
: std::conditional_t<bool(B::value), conjunction<Bs...>, B> {};
template < typename... Bs >
constexpr bool conjunction_v = conjunction<Bs...>::value;
//
// disjunction, disjunction_v
//
template < typename... >
struct disjunction
: std::false_type {};
template < typename B >
struct disjunction<B>
: B {};
template < typename B, typename... Bs>
struct disjunction<B, Bs...>
: std::conditional_t<bool(B::value), B, disjunction<Bs...>> {};
template < typename... Bs >
constexpr bool disjunction_v = disjunction<Bs...>::value;
//
// bool_constant
//
template < bool B >
using bool_constant = std::integral_constant<bool, B>;
//
// negation, negation_v
//
template < typename B >
struct negation
: bool_constant<!bool(B::value)> {};
template < typename B >
constexpr bool negation_v = negation<B>::value;
}
}
}
namespace kari
{
template < std::size_t N, typename F, typename... Args >
struct curry_t;
namespace detail
{
template
<
std::size_t N, typename F, typename... Args,
typename std::enable_if<std_ext::conjunction_v<
std_ext::bool_constant<(N == 0)>,
std_ext::is_invocable<F, Args...>
>, int>::type = 0
>
constexpr auto make_curry(F&& f, std::tuple<Args...>&& args) {
return std_ext::apply(std::forward<F>(f), std::move(args));
}
template
<
std::size_t N, typename F, typename... Args,
typename std::enable_if<std_ext::disjunction_v<
std_ext::bool_constant<(N > 0)>,
std_ext::negation<std_ext::is_invocable<F, Args...>>
>, int>::type = 0
>
constexpr decltype(auto) make_curry(F&& f, std::tuple<Args...>&& args) {
return curry_t<
N,
std::decay_t<F>,
Args...
>(std::forward<F>(f), std::move(args));
}
template < std::size_t N, typename F >
constexpr decltype(auto) make_curry(F&& f) {
return make_curry<N>(std::forward<F>(f), std::make_tuple());
}
}
template < std::size_t N, typename F, typename... Args >
struct curry_t final {
template < typename U >
constexpr curry_t(U&& u, std::tuple<Args...>&& args)
: f_(std::forward<U>(u))
, args_(std::move(args)) {}
// min_arity
constexpr std::size_t min_arity() const noexcept {
return N;
}
// recurry
template < std::size_t M >
constexpr decltype(auto) recurry() && {
return detail::make_curry<M>(
std::move(f_),
std::move(args_));
}
template < std::size_t M >
constexpr decltype(auto) recurry() const & {
auto self_copy = *this;
return std::move(self_copy).template recurry<M>();
}
// operator(As&&...)
constexpr decltype(auto) operator()() && {
return std::move(*this).template recurry<0>();
}
template < typename A >
constexpr decltype(auto) operator()(A&& a) && {
return detail::make_curry<(N > 0 ? N - 1 : 0)>(
std::move(f_),
std::tuple_cat(
std::move(args_),
std::make_tuple(std::forward<A>(a))));
}
template < typename A, typename... As >
constexpr decltype(auto) operator()(A&& a, As&&... as) && {
return std::move(*this)(std::forward<A>(a))(std::forward<As>(as)...);
}
template < typename... As >
constexpr decltype(auto) operator()(As&&... as) const & {
auto self_copy = *this;
return std::move(self_copy)(std::forward<As>(as)...);
}
private:
F f_;
std::tuple<Args...> args_;
};
//
// is_curried, is_curried_v
//
namespace detail
{
template < typename F >
struct is_curried_impl
: std::false_type {};
template < std::size_t N, typename F, typename... Args >
struct is_curried_impl<curry_t<N, F, Args...>>
: std::true_type {};
}
template < typename F >
struct is_curried
: detail::is_curried_impl<std::remove_cv_t<std::remove_reference_t<F>>> {};
template < typename F >
constexpr bool is_curried_v = is_curried<F>::value;
//
// curry
//
template
<
typename F,
typename std::enable_if<is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curry(F&& f) {
return std::forward<F>(f);
}
template
<
typename F,
typename std::enable_if<!is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curry(F&& f) {
return detail::make_curry<0>(std::forward<F>(f));
}
template < typename F, typename... Args >
constexpr decltype(auto) curry(F&& f, Args&&... args) {
return curry(std::forward<F>(f))(std::forward<Args>(args)...);
}
//
// curryV
//
template
<
typename F,
typename std::enable_if<is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curryV(F&& f) {
constexpr auto n = std::numeric_limits<std::size_t>::max();
return std::forward<F>(f).template recurry<n>();
}
template
<
typename F,
typename std::enable_if<!is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curryV(F&& f) {
constexpr auto n = std::numeric_limits<std::size_t>::max();
return detail::make_curry<n>(std::forward<F>(f));
}
template < typename F, typename... Args >
constexpr decltype(auto) curryV(F&& f, Args&&... args) {
return curryV(std::forward<F>(f))(std::forward<Args>(args)...);
}
//
// curryN
//
template
<
std::size_t N, typename F,
typename std::enable_if<is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curryN(F&& f) {
return std::forward<F>(f).template recurry<N>();
}
template
<
std::size_t N, typename F,
typename std::enable_if<!is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) curryN(F&& f) {
return detail::make_curry<N>(std::forward<F>(f));
}
template < std::size_t N, typename F, typename... Args >
constexpr decltype(auto) curryN(F&& f, Args&&... args) {
static_assert(
std::numeric_limits<std::size_t>::max() - sizeof...(Args) >= N,
"N too big");
return curryN<N + sizeof...(Args)>(std::forward<F>(f))(std::forward<Args>(args)...);
}
}
namespace kari
{
//
// fid
//
struct fid_t {
template < typename A >
constexpr decltype(auto) operator()(A&& a) const {
return std::forward<A>(a);
}
};
constexpr auto fid = curry(fid_t{});
//
// fconst
//
struct fconst_t {
template < typename A, typename B >
decltype(auto) operator()(A&& a, B&& b) const {
(void)b;
return std::forward<A>(a);
}
};
constexpr auto fconst = curry(fconst_t{});
//
// fflip
//
struct fflip_t {
template < typename F, typename A, typename B >
constexpr decltype(auto) operator()(F&& f, A&& a, B&& b) const {
return curry(
std::forward<F>(f),
std::forward<B>(b),
std::forward<A>(a));
}
};
constexpr auto fflip = curry(fflip_t{});
//
// fcompose
//
struct fcompose_t {
template < typename G, typename F, typename A >
constexpr decltype(auto) operator()(G&& g, F&& f, A&& a) const {
return curry(
std::forward<G>(g),
curry(
std::forward<F>(f),
std::forward<A>(a)));
}
};
constexpr auto fcompose = curry(fcompose_t{});
//
// fcompose operators
//
template
<
typename G, typename F,
typename std::enable_if<is_curried_v<G>, int>::type = 0,
typename std::enable_if<is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) operator*(G&& g, F&& f) {
return fcompose(
std::forward<G>(g),
std::forward<F>(f));
}
template
<
typename F, typename A,
typename std::enable_if< is_curried_v<F>, int>::type = 0,
typename std::enable_if<!is_curried_v<A>, int>::type = 0
>
constexpr decltype(auto) operator*(F&& f, A&& a) {
return std::forward<F>(f)(std::forward<A>(a));
}
template
<
typename A, typename F,
typename std::enable_if<!is_curried_v<A>, int>::type = 0,
typename std::enable_if< is_curried_v<F>, int>::type = 0
>
constexpr decltype(auto) operator*(A&& a, F&& f) {
return std::forward<F>(f)(std::forward<A>(a));
}
}
namespace kari
{
namespace underscore
{
struct us_t {};
constexpr us_t _{};
// `op` _
constexpr auto operator - (us_t) { return curry(std::negate<>()); }
constexpr auto operator ~ (us_t) { return curry(std::bit_not<>()); }
constexpr auto operator ! (us_t) { return curry(std::logical_not<>()); }
// _ `op` _
constexpr auto operator + (us_t, us_t) { return curry(std::plus<>()); }
constexpr auto operator - (us_t, us_t) { return curry(std::minus<>()); }
constexpr auto operator * (us_t, us_t) { return curry(std::multiplies<>()); }
constexpr auto operator / (us_t, us_t) { return curry(std::divides<>()); }
constexpr auto operator % (us_t, us_t) { return curry(std::modulus<>()); }
constexpr auto operator < (us_t, us_t) { return curry(std::less<>()); }
constexpr auto operator > (us_t, us_t) { return curry(std::greater<>()); }
constexpr auto operator <= (us_t, us_t) { return curry(std::less_equal<>()); }
constexpr auto operator >= (us_t, us_t) { return curry(std::greater_equal<>()); }
constexpr auto operator | (us_t, us_t) { return curry(std::bit_or<>()); }
constexpr auto operator & (us_t, us_t) { return curry(std::bit_and<>()); }
constexpr auto operator ^ (us_t, us_t) { return curry(std::bit_xor<>()); }
constexpr auto operator || (us_t, us_t) { return curry(std::logical_or<>()); }
constexpr auto operator && (us_t, us_t) { return curry(std::logical_and<>()); }
// A `op` _
template < typename A > constexpr auto operator + (A&& a, us_t) { return (_ + _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator - (A&& a, us_t) { return (_ - _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator * (A&& a, us_t) { return (_ * _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator / (A&& a, us_t) { return (_ / _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator % (A&& a, us_t) { return (_ % _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator < (A&& a, us_t) { return (_ < _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator > (A&& a, us_t) { return (_ > _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator <= (A&& a, us_t) { return (_ <= _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator >= (A&& a, us_t) { return (_ >= _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator | (A&& a, us_t) { return (_ | _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator & (A&& a, us_t) { return (_ & _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator ^ (A&& a, us_t) { return (_ ^ _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator || (A&& a, us_t) { return (_ || _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator && (A&& a, us_t) { return (_ && _)(std::forward<A>(a)); }
// _ `op` A
template < typename A > constexpr auto operator + (us_t, A&& a) { return fflip(_ + _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator - (us_t, A&& a) { return fflip(_ - _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator * (us_t, A&& a) { return fflip(_ * _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator / (us_t, A&& a) { return fflip(_ / _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator % (us_t, A&& a) { return fflip(_ % _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator < (us_t, A&& a) { return fflip(_ < _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator > (us_t, A&& a) { return fflip(_ > _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator <= (us_t, A&& a) { return fflip(_ <= _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator >= (us_t, A&& a) { return fflip(_ >= _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator | (us_t, A&& a) { return fflip(_ | _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator & (us_t, A&& a) { return fflip(_ & _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator ^ (us_t, A&& a) { return fflip(_ ^ _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator || (us_t, A&& a) { return fflip(_ || _)(std::forward<A>(a)); }
template < typename A > constexpr auto operator && (us_t, A&& a) { return fflip(_ && _)(std::forward<A>(a)); }
}
}
// Local Variables:
// indent-tabs-mode: nil
// End:

657
tests.cpp Normal file
View File

@@ -0,0 +1,657 @@
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "kari.hpp"
#include <cstdio>
#include <cstring>
namespace
{
const auto _42_tl = []() {
return 42;
};
const auto id_tl = [](int v) {
return v;
};
const auto minus2_tl = [](int v1, int v2) {
return v1 - v2;
};
const auto minus3_tl = [](int v1, int v2, int v3) {
return v1 - v2 - v3;
};
const auto minus4_tl = [](int v1, int v2, int v3, int v4) {
return v1 - v2 - v3 - v4;
};
}
namespace
{
const auto id_gl = [](auto&& v) {
return v;
};
const auto minus2_gl = [](auto&& v1, auto&& v2) {
return v1 - v2;
};
const auto minus3_gl = [](auto&& v1, auto&& v2, auto&& v3) {
return v1 - v2 - v3;
};
const auto minus4_gl = [](auto&& v1, auto&& v2, auto&& v3, auto&& v4) {
return v1 - v2 - v3 - v4;
};
}
namespace
{
struct id_gf {
template < typename T >
constexpr auto operator()(T&& v) const {
return v;
}
};
struct minus2_gf {
template < typename T1, typename T2 >
constexpr auto operator()(T1&& v1, T2&& v2) const {
return v1 - v2;
}
};
struct minus3_gf {
template < typename T1, typename T2, typename T3 >
constexpr auto operator()(T1&& v1, T2&& v2, T3&& v3) const {
return v1 - v2 - v3;
}
};
struct plusV_gf {
template < typename A >
constexpr decltype(auto) operator()(A&& a) const {
return std::forward<A>(a);
}
template < typename A, typename B >
constexpr decltype(auto) operator()(A&& a, B&& b) const {
return std::forward<A>(a) + std::forward<B>(b);
}
template < typename A, typename B, typename... Cs >
constexpr decltype(auto) operator()(A&& a, B&& b, Cs&&... cs) const {
return (*this)(
(*this)(std::forward<A>(a), std::forward<B>(b)),
std::forward<Cs>(cs)...);
}
};
constexpr auto f = kari::curry(minus2_gf{}, 10, 20);
}
namespace
{
struct box {
box(int v) : v_(v) {}
~box() {
v_ = 100500;
}
box(box&& o) : v_(o.v_) {
o.v_ = 100500;
++moveCount_;
}
box(const box& o) : v_(o.v_) {
++copyCount_;
}
box& operator=(box&& o) = delete;
box& operator=(const box&) = delete;
int v() const { return v_; }
int addV(int add) {
v_ += add;
return v_;
}
static void resetCounters() {
moveCount_ = 0;
copyCount_ = 0;
}
static int moveCount() {
return moveCount_;
}
static int copyCount() {
return copyCount_;
}
private:
int v_;
static int moveCount_;
static int copyCount_;
};
int box::moveCount_;
int box::copyCount_;
static box operator+(const box& lhs, const box& rhs) {
return box(lhs.v() + rhs.v());
}
static box operator-(const box& lhs, const box& rhs) {
return box(lhs.v() - rhs.v());
}
}
namespace
{
struct box_without_move {
box_without_move(int v) : v_(v) {}
~box_without_move() {
v_ = 100500;
}
box_without_move(const box_without_move& o) : v_(o.v_) {
++copyCount_;
}
box_without_move& operator=(const box_without_move&) = delete;
int v() const { return v_; }
int addV(int add) {
v_ += add;
return v_;
}
static void resetCounters() {
copyCount_ = 0;
}
static int copyCount() {
return copyCount_;
}
private:
int v_;
static int copyCount_;
};
int box_without_move::copyCount_;
static box_without_move operator+(const box_without_move& lhs, const box_without_move& rhs) {
return box_without_move(lhs.v() + rhs.v());
}
static box_without_move operator-(const box_without_move& lhs, const box_without_move& rhs) {
return box_without_move(lhs.v() - rhs.v());
}
}
using namespace kari;
TEST_CASE("kari_feature") {
SECTION("underscore") {
using namespace kari::underscore;
REQUIRE((-_)(40) == -40);
REQUIRE((_ + 40)(2) == 42);
REQUIRE((_ - 2)(44) == 42);
REQUIRE((_ * 21)(2) == 42);
REQUIRE((_ / 2)(84) == 42);
REQUIRE((_ % 100)(142) == 42);
REQUIRE((40 + _)(2) == 42);
REQUIRE((44 - _)(2) == 42);
REQUIRE((21 * _)(2) == 42);
REQUIRE((84 / _)(2) == 42);
REQUIRE((142 % _)(100) == 42);
REQUIRE((_ + _)(40,2) == 42);
REQUIRE((_ - _)(44,2) == 42);
REQUIRE((_ * _)(21,2) == 42);
REQUIRE((_ / _)(84,2) == 42);
REQUIRE((_ % _)(142,100) == 42);
}
SECTION("ref_functor") {
REQUIRE(curry(minus3_gf())(1,2,3) == -4);
{
auto gf1 = id_gf();
REQUIRE(curry(gf1)(1) == 1);
auto gf2 = minus2_gf();
REQUIRE(curry(gf2)(1,2) == -1);
auto gf3 = minus3_gf();
REQUIRE(curry(gf3)(1,2,3) == -4);
}
{
const auto gf1 = id_gf();
REQUIRE(curry(gf1)(1) == 1);
const auto gf2 = minus2_gf();
REQUIRE(curry(gf2)(1,2) == -1);
const auto gf3 = minus3_gf();
REQUIRE(curry(gf3)(1,2,3) == -4);
}
}
SECTION("ref_forwarding") {
{
int i = 42;
const int& g = curry([](const int& v, int){
return std::ref(v);
}, std::ref(i), 0);
REQUIRE(&i == &g);
}
{
int i = 42;
const int& g = curry([](int& v, int){
return std::ref(v);
}, std::ref(i), 0);
REQUIRE(&i == &g);
}
}
SECTION("move_vs_copy") {
{
box::resetCounters();
const auto b1 = box(1);
const auto b2 = box(2);
const auto b3 = box(3);
const auto b4 = box(4);
const auto b5 = box(5);
curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
})(std::move(b1))(std::move(b2))(std::move(b3))(std::move(b4))(std::move(b5));
REQUIRE(box::moveCount() == 25);
REQUIRE(box::copyCount() == 5);
}
{
box::resetCounters();
auto b1 = box(1);
auto b2 = box(2);
auto b3 = box(3);
auto b4 = box(4);
auto b5 = box(5);
curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
})(std::move(b1))(std::move(b2))(std::move(b3))(std::move(b4))(std::move(b5));
REQUIRE(box::moveCount() == 30);
REQUIRE(box::copyCount() == 0);
}
{
box::resetCounters();
curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
})(box(1))(box(2))(box(3))(box(4))(box(5));
REQUIRE(box::moveCount() == 30);
REQUIRE(box::copyCount() == 0);
}
{
box::resetCounters();
curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
})(box(1),box(2))(box(3),box(4))(box(5));
REQUIRE(box::moveCount() == 30);
REQUIRE(box::copyCount() == 0);
}
{
box::resetCounters();
curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
})(box(1),box(2),box(3),box(4),box(5));
REQUIRE(box::moveCount() == 30);
REQUIRE(box::copyCount() == 0);
}
{
box::resetCounters();
const auto c0 = curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
});
const auto c1 = c0(box(1));
const auto c2 = c1(box(2));
const auto c3 = c2(box(3));
const auto c4 = c3(box(4));
const auto c5 = c4(box(5));
REQUIRE(c5.v() == 15);
REQUIRE(box::moveCount() == 30);
REQUIRE(box::copyCount() == 10);
}
{
box_without_move::resetCounters();
const auto c0 = curry([](auto&& v1, auto&& v2, auto&& v3, auto&& v4, auto&& v5){
return v1 + v2 + v3 + v4 + v5;
});
const auto c1 = c0(box_without_move(1));
const auto c2 = c1(box_without_move(2));
const auto c3 = c2(box_without_move(3));
const auto c4 = c3(box_without_move(4));
const auto c5 = c4(box_without_move(5));
REQUIRE(c5.v() == 15);
REQUIRE(box_without_move::copyCount() == 40);
}
{
box::resetCounters();
curryV(plusV_gf())(box(1),box(2),box(3),box(4),box(5))();
REQUIRE(box::moveCount() == 35);
REQUIRE(box::copyCount() == 0);
}
{
box::resetCounters();
const auto c0 = curryV(plusV_gf());
const auto c1 = c0(box(1));
const auto c2 = c1(box(2));
const auto c3 = c2(box(3));
const auto c4 = c3(box(4));
const auto c5 = c4(box(5));
REQUIRE(c5().v() == 15);
REQUIRE(box::moveCount() == 35);
REQUIRE(box::copyCount() == 15);
}
}
SECTION("persistent") {
auto c = curry(minus3_gl);
auto c10 = c(box(10));
auto c10_2 = c10(box(2));
auto c10_2_3 = c10_2(box(3));
auto c10_2_3d = c10_2(box(3));
REQUIRE(c10_2_3.v() == 5);
REQUIRE(c10_2_3.v() == c10_2_3d.v());
auto c9 = c(box(9));
auto c9_1 = c9(box(1));
auto c9_1_2 = c9_1(box(2));
auto c9_1_2d = c9_1(box(2));
REQUIRE(c9_1_2.v() == 6);
REQUIRE(c9_1_2.v() == c9_1_2d.v());
auto c10_3 = c10(box(3));
auto c10_3_5 = c10_3(box(5));
auto c10_3_5d = c10_3(box(5));
REQUIRE(c10_3_5.v() == 2);
REQUIRE(c10_3_5.v() == c10_3_5d.v());
}
}
TEST_CASE("kari") {
SECTION("arity/min_arity") {
REQUIRE(curry(minus3_gl).min_arity() == 0);
REQUIRE(curryV(plusV_gf()).min_arity() == std::numeric_limits<std::size_t>::max());
REQUIRE(curryN<3>(plusV_gf()).min_arity() == 3);
REQUIRE(curryN<3>(plusV_gf())(1).min_arity() == 2);
REQUIRE(curryN<3>(plusV_gf())(1)(2).min_arity() == 1);
}
SECTION("arity/recurring") {
constexpr auto max_size_t = std::numeric_limits<std::size_t>::max();
{
REQUIRE(curry(curry(minus3_gl)).min_arity() == 0);
REQUIRE(curry(curryV(plusV_gf())).min_arity() == max_size_t);
REQUIRE(curry(curryN<3>(plusV_gf())).min_arity() == 3);
}
{
REQUIRE(curryV(curry(minus3_gl)).min_arity() == max_size_t);
REQUIRE(curryV(curryV(plusV_gf())).min_arity() == max_size_t);
REQUIRE(curryV(curryN<3>(plusV_gf())).min_arity() == max_size_t);
}
{
REQUIRE(curryN<2>(curry(minus3_gl)).min_arity() == 2);
REQUIRE(curryN<2>(curryV(plusV_gf())).min_arity() == 2);
REQUIRE(curryN<2>(curryN<3>(plusV_gf())).min_arity() == 2);
}
}
SECTION("is_curried") {
static_assert(!is_curried_v<void(int)>, "static unit test error");
static_assert(!is_curried_v<decltype(minus2_gl)>, "static unit test error");
static_assert(is_curried_v<decltype(curry(minus2_gl))>, "static unit test error");
auto c = curry(minus3_gl);
static_assert(is_curried_v<decltype(c)>, "static unit test error");
auto c10 = c(box(10));
static_assert(is_curried_v<decltype(c10)>, "static unit test error");
const auto c10_2 = c10(box(2));
static_assert(is_curried_v<decltype(c10_2)>, "static unit test error");
auto c10_2_3 = c10_2(box(3));
static_assert(!is_curried_v<decltype(c10_2_3)>, "static unit test error");
REQUIRE(c10_2_3.v() == 5);
}
SECTION("typed_lambdas/simple") {
REQUIRE(curry(_42_tl) == 42);
REQUIRE(curry(id_tl)(42) == 42);
REQUIRE(curry(minus2_tl)(8,4) == 4);
REQUIRE(curry(minus3_tl)(8,5,2) == 1);
}
SECTION("typed_lambdas/one_by_one_calling") {
REQUIRE(curry(id_tl)(42) == 42);
REQUIRE(curry(minus2_tl)(8)(4) == 4);
REQUIRE(curry(minus3_tl)(8)(5)(2) == 1);
}
SECTION("typed_lambdas/combined_calling") {
REQUIRE(curry(minus3_tl)(8,5)(2) == 1);
REQUIRE(curry(minus3_tl)(8)(5,2) == 1);
REQUIRE(curry(minus4_tl)(14,2)(3)(4) == 5);
REQUIRE(curry(minus4_tl)(14)(2,3)(4) == 5);
REQUIRE(curry(minus4_tl)(14)(2)(3,4) == 5);
REQUIRE(curry(minus4_tl)(14,2)(3,4) == 5);
REQUIRE(curry(minus4_tl)(14,2,3)(4) == 5);
REQUIRE(curry(minus4_tl)(14)(2,3,4) == 5);
}
SECTION("generic_lambdas/simple") {
REQUIRE(curry(id_gl)(42) == 42);
REQUIRE(curry(minus2_gl)(8,4) == 4);
REQUIRE(curry(minus3_gl)(8,5,2) == 1);
}
SECTION("generic_lambdas/one_by_one_calling") {
REQUIRE(curry(id_gl)(42) == 42);
REQUIRE(curry(minus2_gl)(8)(4) == 4);
REQUIRE(curry(minus3_gl)(8)(5)(2) == 1);
}
SECTION("generic_lambdas/combined_calling") {
REQUIRE(curry(minus3_gl)(8,5)(2) == 1);
REQUIRE(curry(minus3_gl)(8)(5,2) == 1);
REQUIRE(curry(minus4_gl)(14,2)(3)(4) == 5);
REQUIRE(curry(minus4_gl)(14)(2,3)(4) == 5);
REQUIRE(curry(minus4_gl)(14)(2)(3,4) == 5);
REQUIRE(curry(minus4_gl)(14,2)(3,4) == 5);
REQUIRE(curry(minus4_gl)(14,2,3)(4) == 5);
REQUIRE(curry(minus4_gl)(14)(2,3,4) == 5);
}
SECTION("nested_lambdas/full_apply") {
{
REQUIRE(curry([](){
return _42_tl;
})() == 42);
//
REQUIRE(curry([](){
return curry(_42_tl);
}) == 42);
}
{
REQUIRE(curry([](){
return id_gl;
})(9) == 9);
//
REQUIRE(curry([](){
return curry(id_gl);
})(9) == 9);
}
{
REQUIRE(curry([](){
return minus2_gl;
})(9,5) == 4);
//
REQUIRE(curry([](){
return curry(minus2_gl);
})(9,5) == 4);
REQUIRE(curry([](){
return curry(minus2_gl);
})(9)(5) == 4);
//
REQUIRE(curry([](){
return curry(minus3_gl);
})(9,4,3) == 2);
REQUIRE(curry([](){
return curry(minus3_gl);
})(9)(4)(3) == 2);
REQUIRE(curry([](){
return curry(minus3_gl);
})(9,4)(3) == 2);
REQUIRE(curry([](){
return curry(minus3_gl);
})(9)(4,3) == 2);
}
}
SECTION("variadic_functions") {
{
const auto c = curry(plusV_gf());
REQUIRE(c(15) == 15);
REQUIRE(curry(plusV_gf(), 6) == 6);
}
{
const auto c = curryV(plusV_gf());
REQUIRE(c(1,2,3,4,5)() == 15);
REQUIRE(curryV(plusV_gf(), 1, 2, 3)() == 6);
}
{
const auto c = curryN<3>(plusV_gf());
REQUIRE(c(1,2,3) == 6);
REQUIRE(curryN<0>(plusV_gf(), 1, 2, 3) == 6);
}
{
char buffer[256] = {'\0'};
auto c = kari::curryV(std::snprintf, buffer, 256, "%d + %d = %d");
c(37, 5, 42)();
REQUIRE(std::strcmp("37 + 5 = 42", buffer) == 0);
}
{
char buffer[256] = {'\0'};
auto c = kari::curryN<3>(std::snprintf, buffer, 256, "%d + %d = %d");
c(37, 5, 42);
REQUIRE(std::strcmp("37 + 5 = 42", buffer) == 0);
}
}
SECTION("variadic_functions/recurring") {
{
auto c0 = curry(curry(plusV_gf()));
auto c1 = curry(curryV(plusV_gf()));
auto c2 = curry(curryN<3>(plusV_gf()));
REQUIRE(c0(42) == 42);
REQUIRE(c1(40,2)() == 42);
REQUIRE(c2(37,2,3) == 42);
}
{
auto c0 = curryV(curry(plusV_gf()));
auto c1 = curryV(curryV(plusV_gf()));
auto c2 = curryV(curryN<3>(plusV_gf()));
REQUIRE(c0(40,2)() == 42);
REQUIRE(c1(40,2)() == 42);
REQUIRE(c2(40,2)() == 42);
}
{
auto c0 = curryN<2>(curry(plusV_gf()));
auto c1 = curryN<2>(curryV(plusV_gf()));
auto c2 = curryN<2>(curryN<3>(plusV_gf()));
auto c3 = curryN<4>(curryN<3>(plusV_gf()));
REQUIRE(c0(40,2) == 42);
REQUIRE(c1(40,2) == 42);
REQUIRE(c2(40,2) == 42);
REQUIRE(c3(20,15,5,2) == 42);
}
}
}
TEST_CASE("kari_helpers") {
SECTION("fid") {
REQUIRE(fid(box(10)).v() == 10);
}
SECTION("fconst") {
REQUIRE(fconst(box(10), 20).v() == 10);
{
auto b10 = fconst(box(10));
REQUIRE(b10(20).v() == 10);
REQUIRE(std::move(b10)(30).v() == 10);
REQUIRE(b10(20).v() == 100500);
}
}
SECTION("fflip") {
REQUIRE(fflip(curry(std::minus<>()))(10, 20) == 10);
REQUIRE(fflip(minus3_gl)(10,20,50) == -40);
}
SECTION("fcompose") {
using namespace kari::underscore;
REQUIRE(fcompose(_+2, _*2, 4) == 10);
REQUIRE((_+2) * (_*2) * 4 == 10);
REQUIRE(4 * (_+2) * (_*2) == 12);
{
const auto s3 = [](int v1, int v2, int v3){
return v1 + v2 + v3;
};
const auto c = curry(s3) * (_*2) * 10 * 20 * 30;
REQUIRE(c == 70);
}
{
// (. (+2)) (*2) $ 10 == 24
int i = fflip(fcompose)(_+2, _*2, 10);
int j = (_*(_+2))(_*2)(10);
REQUIRE(i == 24);
REQUIRE(j == 24);
}
{
// ((+2) .) (*2) $ 10 == 24
int i = fcompose(_+2)(_*2, 10);
int j = ((_+2) * _)(_*2)(10);
REQUIRE(i == 22);
REQUIRE(j == 22);
}
}
}
namespace kari_regression {
namespace change_type_after_applying {
template < typename C >
struct first_type_impl;
template < std::size_t N, typename F, typename A, typename... Args >
struct first_type_impl<curry_t<N, F, A, Args...>> {
using type = A;
};
template < typename C >
struct first_type
: first_type_impl<std::remove_cv_t<std::remove_reference_t<C>>> {};
}
}
TEST_CASE("kari_regression") {
SECTION("change_type_after_applying") {
using namespace kari_regression::change_type_after_applying;
const auto f3 = [](int& v1, int v2, int v3){
return v1 + v2 + v3;
};
int i = 0;
auto c0 = curry(f3, 0);
auto c1 = c0(std::ref(i));
auto c2 = std::move(c0)(std::ref(i));
static_assert(std::is_same<
typename first_type<decltype(c1)>::type,
typename first_type<decltype(c2)>::type
>::value,"static unit test error");
}
SECTION("curryN_already_curried_function") {
auto c = curryN<3>(plusV_gf());
auto c2 = curryN<3>(c);
REQUIRE(c2(1,2,3) == 6);
}
}
// Local Variables:
// indent-tabs-mode: nil
// End: