Skip to content

Commit 6166be9

Browse files
committed
Support range-like non-awaitable coroutines
1 parent eaeb89c commit 6166be9

File tree

2 files changed

+82
-13
lines changed

2 files changed

+82
-13
lines changed

include/trompeloeil/coro.hpp

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,22 @@
1414
#ifndef TROMPELOEIL_CORO_HPP
1515
#define TROMPELOEIL_CORO_HPP
1616

17+
#if __cplusplus < 202002L
18+
# error "C++20 is required"
19+
#endif
20+
1721
#if defined(__cpp_impl_coroutine)
1822
# define TROMPELOEIL_COROUTINES_SUPPORTED 1
1923
#else
2024
# error "Coroutines are not supported by this compiler"
2125
#endif
2226

27+
#include <version>
28+
#if defined(__cpp_lib_ranges)
29+
# define TROMPELOEIL_RANGES_SUPPORTED 1
30+
# include <ranges>
31+
#endif
32+
2333
#ifdef TROMPELOEIL_COROUTINES_SUPPORTED
2434

2535
#ifndef TROMPELOEIL_MOCK_HPP_
@@ -41,22 +51,30 @@ namespace trompeloeil
4151
};
4252

4353
template <typename T>
44-
struct coro_value_type
54+
struct coro_value_type;
55+
56+
template <typename T>
57+
requires(requires (T coro) { coro.operator co_await().await_resume(); })
58+
struct coro_value_type<T>
4559
{
46-
static auto func()
47-
{
48-
if constexpr (requires {std::declval<T>().operator co_await();})
49-
{
50-
return type_wrapper<decltype(std::declval<T>().operator co_await().await_resume())>{};
51-
}
52-
else
53-
{
54-
return type_wrapper<decltype(std::declval<T>().await_resume())>{};
55-
}
56-
}
57-
using type = typename decltype(func())::type;
60+
using type = typename type_wrapper<decltype(std::declval<T>().operator co_await().await_resume())>::type;
61+
};
62+
63+
template <typename T>
64+
requires(requires (T coro) { coro.await_resume(); })
65+
struct coro_value_type<T>
66+
{
67+
using type = typename type_wrapper<decltype(std::declval<T>().await_resume())>::type;
5868
};
5969

70+
#ifdef TROMPELOEIL_RANGES_SUPPORTED
71+
template <std::ranges::input_range T>
72+
struct coro_value_type<T>
73+
{
74+
using type = typename type_wrapper<std::ranges::range_value_t<T>>::type;
75+
};
76+
#endif
77+
6078
template <typename T>
6179
using coro_value_type_t = typename coro_value_type<T>::type;
6280

test/test_co_mock.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
#include "test_reporter.hpp"
2626
#include <optional>
2727

28+
#include <version>
29+
#ifdef __cpp_lib_generator
30+
#include <generator>
31+
#endif
32+
2833
using trompeloeil::_;
2934

3035
namespace {
@@ -35,6 +40,10 @@ namespace {
3540
MAKE_MOCK0 (voidret, coro::task<void>());
3641
MAKE_MOCK1 (unique, coro::task<iptr>(iptr));
3742
MAKE_MOCK0 (gen, coro::generator<int>());
43+
44+
#ifdef __cpp_lib_generator
45+
MAKE_MOCK0 (stdgen, std::generator<int>());
46+
#endif // __cpp_lib_generator
3847
};
3948
}
4049

@@ -198,4 +207,46 @@ TEST_CASE_METHOD(
198207
REQUIRE(v == 3);
199208
REQUIRE(reports.empty());
200209
}
210+
211+
#ifdef __cpp_lib_generator
212+
TEST_CASE_METHOD(
213+
Fixture,
214+
"CO_YIELD with std::generator",
215+
"[coro]")
216+
{
217+
co_mock m;
218+
REQUIRE_CALL(m, stdgen())
219+
.CO_YIELD(5)
220+
.CO_YIELD(8)
221+
.CO_YIELD(3)
222+
.CO_YIELD(0)
223+
.CO_RETURN();
224+
225+
auto gen = m.stdgen();
226+
227+
SECTION("as iterator")
228+
{
229+
auto it = std::ranges::begin(gen);
230+
REQUIRE(*it == 5);
231+
++it;
232+
REQUIRE(it != std::ranges::end(gen));
233+
REQUIRE(*it == 8);
234+
++it;
235+
REQUIRE(it != std::ranges::end(gen));
236+
REQUIRE(*it == 3);
237+
++it;
238+
REQUIRE(it != std::ranges::end(gen));
239+
REQUIRE(*it == 0);
240+
++it;
241+
REQUIRE(it == std::ranges::end(gen));
242+
}
243+
244+
SECTION("as range")
245+
{
246+
REQUIRE(std::ranges::equal(gen, std::array{5, 8, 3, 0}));
247+
}
248+
249+
REQUIRE(reports.empty());
250+
}
251+
#endif // __cpp_lib_generator
201252
#endif

0 commit comments

Comments
 (0)