based

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

commit ce9865aca018985b89bf4f640e42744805df879a
parent 630d79b6b927b251d8d4b2bd975cf8d2533bcc49
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Thu, 1 May 2025 18:29:12 +0200

Functional curry with tests

Diffstat:
M include/based/functional.hpp | +++++++++++++++++++++++++++++++++++++++++
M test/CMakeLists.txt | ++++
A test/source/curry_test.cpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3 files changed, 115 insertions(+), 0 deletions(-)


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

@@ -7,6 +7,47 @@

namespace based
{

template<typename Function, typename... CapturedArgs>
class curried
{
template<typename FFunction, typename... FCapturedArgs>
friend class curried;

Function m_function;
std::tuple<CapturedArgs...> m_captured;

curried(Function function, std::tuple<CapturedArgs...> args)
: m_function(function)
, m_captured(std::move(args))
{
}

public:
curried(Function function, CapturedArgs&&... args) // NOLINT explicit
: m_function(function)
, m_captured(std::forward<CapturedArgs>(args)...)
{
}

template<typename... NewArgs>
auto operator()(NewArgs&&... args) const
{
auto all_args = std::tuple_cat(
m_captured, std::make_tuple(std::forward<NewArgs>(args)...)
);

if constexpr (std::is_invocable_v<Function, CapturedArgs..., NewArgs...>) {
return std::apply(m_function, std::move(all_args));
} else {
return curried<Function, CapturedArgs..., NewArgs...> {
m_function, std::move(all_args)
};
}
}
};

/* ----- Relation modifiers ----- */

template<typename T, Relation<T> Rel>
auto complement(Rel rel)
{

diff --git a/ test/CMakeLists.txt b/ test/CMakeLists.txt

@@ -74,6 +74,10 @@ add_test(buffer_test)

add_test(function_test)
add_test(scopeguard_test)

# ----- Functional -----

add_test(curry_test)

# ---- End-of-file commands ----

add_folders(Test)

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

@@ -0,0 +1,70 @@

#include <catch2/catch_test_macros.hpp>

#include "based/functional.hpp"

// NOLINTBEGIN congnitive-complexity magic-number

auto free_func(int a, double b, int c, double d)
{
return static_cast<int>(a + b + c + d);
};

TEST_CASE("free function", "[functional/curry]")
{
const based::curried curried = free_func;

REQUIRE(curried(1)(2.0)(3)(4.0) == 10);
REQUIRE(curried(1)(2.0, 3)(4.0) == 10);
REQUIRE(curried(1)(2.0, 3, 4.0) == 10);
REQUIRE(curried(1, 2.0)(3, 4.0) == 10);
REQUIRE(curried(1, 2.0, 3)(4.0) == 10);
REQUIRE(curried(1, 2.0, 3, 4.0) == 10);
}

TEST_CASE("lambda", "[functional/curry]")
{
const based::curried curried = [](int a, double b, int c, double d)
{
return static_cast<int>(a + b + c + d);
};

REQUIRE(curried(1)(2.0)(3)(4.0) == 10);
REQUIRE(curried(1)(2.0)(3, 4.0) == 10);
REQUIRE(curried(1)(2.0, 3)(4.0) == 10);
REQUIRE(curried(1)(2.0, 3, 4.0) == 10);
REQUIRE(curried(1, 2.0)(3)(4.0) == 10);
REQUIRE(curried(1, 2.0)(3, 4.0) == 10);
REQUIRE(curried(1, 2.0, 3)(4.0) == 10);
REQUIRE(curried(1, 2.0, 3, 4.0) == 10);
}

TEST_CASE("member function", "[functional/curry]")
{
struct test
{
auto func(int a, double b, int c, double d) const
{
return static_cast<int>(a + b + c + d);
}
};

const based::curried curried = &test::func;
test t;

REQUIRE(curried(std::ref(t))(1)(2.0)(3)(4.0) == 10);
REQUIRE(curried(std::ref(t))(1)(2.0)(3, 4.0) == 10);
REQUIRE(curried(std::ref(t))(1)(2.0, 3)(4.0) == 10);
REQUIRE(curried(std::ref(t))(1)(2.0, 3, 4.0) == 10);
REQUIRE(curried(std::ref(t))(1, 2.0)(3)(4.0) == 10);
REQUIRE(curried(std::ref(t))(1, 2.0)(3, 4.0) == 10);
REQUIRE(curried(std::ref(t))(1, 2.0, 3)(4.0) == 10);
REQUIRE(curried(std::ref(t))(1, 2.0, 3, 4.0) == 10);
REQUIRE(curried(std::ref(t), 1)(2.0)(3)(4.0) == 10);
REQUIRE(curried(std::ref(t), 1)(2.0, 3)(4.0) == 10);
REQUIRE(curried(std::ref(t), 1, 2.0)(3)(4.0) == 10);
REQUIRE(curried(std::ref(t), 1, 2.0, 3)(4.0) == 10);
REQUIRE(curried(std::ref(t), 1, 2.0, 3, 4.0) == 10);
REQUIRE(curried(std::ref(t), 1, 2.0, 3, 4.0) == 10);
}

// NOLINTEND congnitive-complexity magic-number