diff --git a/include/chain/segment.hpp b/include/chain/segment.hpp index 1a67c0a..ef2b8a1 100644 --- a/include/chain/segment.hpp +++ b/include/chain/segment.hpp @@ -26,8 +26,8 @@ class segment { public: /* - An apply operation may inject additional arguments into the segment. The plan is that the - receiver will get sent to apply and this is how cancellation tokens can be injected into an + The invole operation may inject additional arguments into the segment. The plan is that the + receiver will get sent to invoke and this is how cancellation tokens can be injected into an operation. Something like `with_cancellation`. This feature is also used for the `then` operation where the resolve future is injected into @@ -44,10 +44,6 @@ class segment { explicit segment(type, Applicator&& apply, Fs&&... functions) : _functions{std::move(functions)...}, _apply{std::move(apply)} {} - /* - The basic operations should follow those from C++ lambdas, for now default everything. - and see if the compiler gets it correct. - */ segment(const segment&) = default; segment(segment&&) noexcept = default; auto operator=(const segment&) -> segment& = default; @@ -61,7 +57,7 @@ class segment { } /* - The apply function for a segment always returns void. + The invoke function for a segment always returns void. Invoke will check the receiver for cancellation - If not canceled, apply(segment), cancellation is checked before execution of the segment diff --git a/test/segment_tests.cpp b/test/segment_tests.cpp index 2430048..6c66fe5 100644 --- a/test/segment_tests.cpp +++ b/test/segment_tests.cpp @@ -29,80 +29,101 @@ struct mock_receiver { TEST_CASE("Basic segment operations", "[segment]") { SECTION("simple creation with variadic constructor") { - auto sut = - chain::segment{chain::type>{}, [](auto f) { f(); }, []() { return 42; }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; (void)sut; } SECTION("simple creation with tuple constructor") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - std::make_tuple([]() { return 42; })}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + std::make_tuple([]() -> int { return 42; })}; (void)sut; } SECTION("creation with multiple functions") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [](int x) { return x + 1; }, [](int x) { return x * 2; }}; + auto sut = + chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [](int x) -> int { return x + 1; }, [](int x) -> int { return x * 2; }}; (void)sut; } SECTION("creation with empty function tuple") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, std::tuple<>{}}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + std::tuple<>{}}; (void)sut; } } TEST_CASE("Segment copy and move semantics", "[segment]") { SECTION("copy constructor") { - auto original = - chain::segment{chain::type>{}, [](auto f) { f(); }, []() { return 42; }}; - [[maybe_unused]] auto copy{ - original}; // Use direct initialization due to explicit constructor - // Both should be valid and independent + auto original = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; + auto copy{original}; + + auto original_result = std::move(original).result_type_helper(); + CHECK(original_result == 42); + auto copy_result = std::move(copy).result_type_helper(); + CHECK(copy_result == 42); } SECTION("move constructor") { - auto original = - chain::segment{chain::type>{}, [](auto f) { f(); }, []() { return 42; }}; - [[maybe_unused]] auto moved = std::move(original); - // moved should be valid + auto reference = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; + + auto original = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; + + auto moved = std::move(original); + auto moved_result = std::move(moved).result_type_helper(); + CHECK(moved_result == 42); + auto reference_result = std::move(reference).result_type_helper(); + CHECK(reference_result == 42); } SECTION("segment with move-only types") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [m = stlab::move_only(42)]() { return m.member(); }}; - auto moved = std::move(sut); - // moved should be valid + auto reference = chain::segment{ + chain::type>{}, [](auto f) -> void { f(); }, + [m = stlab::move_only(42)]() mutable -> stlab::move_only { return std::move(m); }}; + auto original = chain::segment{ + chain::type>{}, [](auto f) -> void { f(); }, + [m = stlab::move_only(42)]() mutable -> stlab::move_only { return std::move(m); }}; + + auto moved = std::move(original); + auto moved_result = std::move(moved).result_type_helper(); + CHECK(moved_result == stlab::move_only(42)); + auto reference_result = std::move(reference).result_type_helper(); + CHECK(reference_result == stlab::move_only(42)); } } TEST_CASE("Segment result_type_helper", "[segment]") { SECTION("single function returning int") { - auto sut = - chain::segment{chain::type>{}, [](auto f) { f(); }, []() { return 42; }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; auto result = std::move(sut).result_type_helper(); CHECK(result == 42); } SECTION("function chain with transformations") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [](int x) { return x + 1; }, [](int x) { return x * 2; }}; + auto sut = + chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [](int x) -> int { return x + 1; }, [](int x) -> int { return x * 2; }}; auto result = std::move(sut).result_type_helper(5); CHECK(result == 12); // (5 + 1) * 2 = 12 } SECTION("function chain returning string") { - auto sut = - chain::segment{chain::type>{}, [](auto f) { f(); }, - [](int x) { return x * 2; }, [](int x) { return std::to_string(x); }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [](int x) -> int { return x * 2; }, + [](int x) -> std::string { return std::to_string(x); }}; auto result = std::move(sut).result_type_helper(21); CHECK(result == "42"); } SECTION("void returning function") { auto hit = 0; - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [&hit](int x) { hit = x; }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [&hit](int x) -> void { hit = x; }}; std::move(sut).result_type_helper(42); CHECK(hit == 42); } @@ -112,9 +133,9 @@ TEST_CASE("Segment invoke with receiver", "[segment]") { SECTION("invoke with non-canceled receiver") { auto receiver = std::make_shared(); auto hit = 0; - auto sut = - chain::segment{chain::type>{}, [](auto f, auto... args) { f(args...); }, - [&hit](int x) { hit = x; }}; + auto sut = chain::segment{chain::type>{}, + [](auto f, auto... args) -> void { f(args...); }, + [&hit](int x) -> void { hit = x; }}; std::move(sut).invoke(receiver, 42); CHECK(hit == 42); @@ -125,9 +146,9 @@ TEST_CASE("Segment invoke with receiver", "[segment]") { auto receiver = std::make_shared(); receiver->_canceled = true; auto hit = 0; - auto sut = - chain::segment{chain::type>{}, [](auto f, auto... args) { f(args...); }, - [&hit](int x) { hit = x; }}; + auto sut = chain::segment{chain::type>{}, + [](auto f, auto... args) -> void { f(args...); }, + [&hit](int x) -> void { hit = x; }}; std::move(sut).invoke(receiver, 42); CHECK(hit == 0); // Should not execute @@ -135,12 +156,12 @@ TEST_CASE("Segment invoke with receiver", "[segment]") { SECTION("invoke with exception in segment") { auto receiver = std::make_shared(); - auto sut = - chain::segment{chain::type>{}, [](auto f, auto... args) { f(args...); }, - [](int x) { - if (x == 42) throw std::runtime_error("test error"); - return x; - }}; + auto sut = chain::segment{chain::type>{}, + [](auto f, auto... args) -> void { f(args...); }, + [](int x) -> int { + if (x == 42) throw std::runtime_error("test error"); + return x; + }}; std::move(sut).invoke(receiver, 42); CHECK(receiver->_exception != nullptr); @@ -172,10 +193,10 @@ TEST_CASE("Segment invoke with receiver", "[segment]") { SECTION("invoke with chained functions") { auto receiver = std::make_shared(); auto result = 0; - auto sut = - chain::segment{chain::type>{}, [](auto f, auto... args) { f(args...); }, - [](int x) { return x + 1; }, [](int x) { return x * 2; }, - [&result](int x) { result = x; }}; + auto sut = chain::segment{ + chain::type>{}, [](auto f, auto... args) -> void { f(args...); }, + [](int x) -> int { return x + 1; }, [](int x) -> int { return x * 2; }, + [&result](int x) -> void { result = x; }}; std::move(sut).invoke(receiver, 5); CHECK(result == 12); // (5 + 1) * 2 = 12 @@ -185,15 +206,15 @@ TEST_CASE("Segment invoke with receiver", "[segment]") { TEST_CASE("Segment with injected types", "[segment]") { SECTION("segment with int injection") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - []() { return 42; }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + []() -> int { return 42; }}; // Segment should be constructible with injection type (void)sut; } SECTION("segment with multiple injection types") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - []() { return 42; }}; + auto sut = chain::segment{chain::type>{}, + [](auto f) -> void { f(); }, []() -> int { return 42; }}; // Segment should be constructible with multiple injection types (void)sut; } @@ -201,15 +222,16 @@ TEST_CASE("Segment with injected types", "[segment]") { TEST_CASE("Segment edge cases", "[segment]") { SECTION("empty segment with no functions") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, std::tuple<>{}}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + std::tuple<>{}}; // Should be constructible (void)sut; } SECTION("segment with void function") { auto hit = false; - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [&hit]() { hit = true; }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [&hit]() -> void { hit = true; }}; std::move(sut).result_type_helper(); CHECK(hit); } @@ -217,16 +239,17 @@ TEST_CASE("Segment edge cases", "[segment]") { SECTION("segment with multiple void functions") { auto hit1 = false; auto hit2 = false; - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [&hit1]() { hit1 = true; }, [&hit2]() { hit2 = true; }}; + auto sut = + chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [&hit1]() -> void { hit1 = true; }, [&hit2]() -> void { hit2 = true; }}; std::move(sut).result_type_helper(); CHECK(hit1); CHECK(hit2); } SECTION("segment with variadic function") { - auto sut = chain::segment{chain::type>{}, [](auto f) { f(); }, - [](auto... args) { return (args + ...); }}; + auto sut = chain::segment{chain::type>{}, [](auto f) -> void { f(); }, + [](auto... args) -> int { return (args + ...); }}; auto result = std::move(sut).result_type_helper(1, 2, 3, 4); CHECK(result == 10); }