Improvements to fmap() implementation · AIENGINE/cppcoro@8e72893 · GitHub
Skip to content

Commit 8e72893

Browse files
committed
Improvements to fmap() implementation
- Added documentation to README - Implemented fmap() for generator<T>, recursive_generator<T>, shared_task<T>. - Centralised operator| overloads for fmap() in fmap.hpp. - Now provide two-argument fmap() overloads as the type-specific implementations: fmap(func, value) This replaces the need for detail::apply_fmap() functions.
1 parent 2160ab0 commit 8e72893

10 files changed

Lines changed: 330 additions & 77 deletions

README.md

Lines changed: 110 additions & 1 deletion

include/cppcoro/async_generator.hpp

Lines changed: 18 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -653,46 +653,28 @@ namespace cppcoro
653653
}
654654
}
655655

656-
// operator|(async_generator<T>, fmap_transform<F>)
657-
658-
namespace detail
656+
template<typename FUNC, typename T>
657+
async_generator<std::result_of_t<FUNC&(decltype(*std::declval<typename async_generator<T>::iterator&>()))>> fmap(
658+
FUNC func,
659+
async_generator<T> source)
659660
{
660-
template<typename T, typename FUNC>
661-
auto apply_fmap(async_generator<T> g, FUNC func)
662-
-> async_generator<std::result_of_t<FUNC&(typename async_generator<T>::iterator::reference)>>
661+
static_assert(
662+
!std::is_reference_v<FUNC>,
663+
"Passing by reference to async_generator<T> coroutine is unsafe. "
664+
"Use std::ref or std::cref to explicitly pass by reference.");
665+
666+
// Explicitly hand-coding the loop here rather than using range-based
667+
// for loop since it's difficult to std::forward<???> the value of a
668+
// range-based for-loop, preserving the value category of operator*
669+
// return-value.
670+
auto it = co_await source.begin();
671+
const auto itEnd = source.end();
672+
while (it != itEnd)
663673
{
664-
static_assert(
665-
!std::is_reference_v<FUNC>,
666-
"Passing by reference to async_generator<T> coroutine is unsafe. "
667-
"Use std::ref or std::cref to explicitly pass by reference.");
668-
669-
auto it = co_await g.begin();
670-
const auto itEnd = g.end();
671-
while (it != itEnd)
672-
{
673-
co_yield std::invoke(func, *it);
674-
(void)co_await ++it;
675-
}
674+
co_yield std::invoke(func, *it);
675+
(void)co_await ++it;
676676
}
677677
}
678-
679-
template<typename T, typename FUNC>
680-
auto operator|(async_generator<T>&& source, fmap_transform<FUNC>&& transform)
681-
{
682-
return detail::apply_fmap(std::move(source), std::forward<FUNC>(transform.func));
683-
}
684-
685-
template<typename T, typename FUNC>
686-
auto operator|(async_generator<T>&& source, fmap_transform<FUNC>& transform)
687-
{
688-
return detail::apply_fmap(std::move(source), transform.func);
689-
}
690-
691-
template<typename T, typename FUNC>
692-
auto operator|(async_generator<T>&& source, const fmap_transform<FUNC>& transform)
693-
{
694-
return detail::apply_fmap(std::move(source), transform.func);
695-
}
696678
}
697679

698680
#endif

include/cppcoro/fmap.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,27 @@ namespace cppcoro
2626
{
2727
return fmap_transform<FUNC>{ std::forward<FUNC>(func) };
2828
}
29+
30+
template<typename T, typename FUNC>
31+
decltype(auto) operator|(T&& value, fmap_transform<FUNC>&& transform)
32+
{
33+
// Use ADL for finding fmap() overload.
34+
return fmap(std::forward<FUNC>(transform.func), std::forward<T>(value));
35+
}
36+
37+
template<typename T, typename FUNC>
38+
decltype(auto) operator|(T&& value, const fmap_transform<FUNC>& transform)
39+
{
40+
// Use ADL for finding fmap() overload.
41+
return fmap(transform.func, std::forward<T>(value));
42+
}
43+
44+
template<typename T, typename FUNC>
45+
decltype(auto) operator|(T&& value, fmap_transform<FUNC>& transform)
46+
{
47+
// Use ADL for finding fmap() overload.
48+
return fmap(transform.func, std::forward<T>(value));
49+
}
2950
}
3051

3152
#endif

include/cppcoro/generator.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,15 @@ namespace cppcoro
221221
return generator<T>{ coroutine_handle::from_promise(*this) };
222222
}
223223
}
224+
225+
template<typename FUNC, typename T>
226+
generator<std::result_of_t<FUNC&&(T&)>> fmap(FUNC func, generator<T> source)
227+
{
228+
for (auto& value : source)
229+
{
230+
co_yield std::invoke(func, value);
231+
}
232+
}
224233
}
225234

226235
#endif

include/cppcoro/recursive_generator.hpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
// Copyright (c) Lewis Baker
33
// Licenced under MIT license. See LICENSE.txt for details.
44
///////////////////////////////////////////////////////////////////////////////
5-
#ifndef CPPCORO_GENERATOR_HPP_INCLUDED
6-
#define CPPCORO_GENERATOR_HPP_INCLUDED
5+
#ifndef CPPCORO_RECURSIVE_GENERATOR_HPP_INCLUDED
6+
#define CPPCORO_RECURSIVE_GENERATOR_HPP_INCLUDED
7+
8+
#include <cppcoro/generator.hpp>
79

810
#include <experimental/coroutine>
911
#include <type_traits>
1012
#include <utility>
1113
#include <cassert>
14+
#include <functional>
1215

1316
namespace cppcoro
1417
{
@@ -317,6 +320,17 @@ namespace cppcoro
317320
{
318321
a.swap(b);
319322
}
323+
324+
// Note: When applying fmap operator to a recursive_generator we just yield a non-recursive
325+
// generator since we generally won't be using the result in a recursive context.
326+
template<typename FUNC, typename T>
327+
generator<std::result_of_t<FUNC&&(T&)>> fmap(FUNC func, recursive_generator<T> source)
328+
{
329+
for (auto& value : source)
330+
{
331+
co_yield std::invoke(func, value);
332+
}
333+
}
320334
}
321335

322336
#endif

include/cppcoro/shared_task.hpp

Lines changed: 18 additions & 1 deletion

0 commit comments

Comments
 (0)