New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Move coCapture() to libkj #82
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,48 +4,48 @@ | |
|
||
#pragma once | ||
|
||
#include <kj/debug.h> | ||
#include <type_traits> | ||
#include <kj/common.h> | ||
|
||
namespace workerd { | ||
|
||
namespace _ { | ||
|
||
template<typename Functor> | ||
struct CaptureForCoroutine { | ||
kj::Maybe<Functor> maybeFunctor; | ||
|
||
explicit CaptureForCoroutine(Functor&& f) : maybeFunctor(kj::mv(f)) {} | ||
|
||
template<typename ...Args> | ||
using ReturnType = std::invoke_result_t<Functor, Args...>; | ||
|
||
template<typename ...Args> | ||
static auto coInvoke(Functor functor, Args&&... args) -> ReturnType<Args&&...> { | ||
static auto coInvoke(Functor functor, Args&&... args) | ||
-> decltype(functor(kj::fwd<Args>(args)...)) { | ||
// Since the functor is now in the local scope and no longer a member variable, it will be | ||
// persisted in the coroutine state. | ||
|
||
// Note that `co_await localFunctor(...)` can still return `void`. It just happens that | ||
// Note that `co_await functor(...)` can still return `void`. It just happens that | ||
// `co_return voidReturn();` is explicitly allowed. | ||
co_return co_await functor(kj::fwd<Args>(args)...); | ||
} | ||
|
||
template<typename ...Args> | ||
auto operator()(Args&&... args) { | ||
auto localFunctor = kj::mv(KJ_REQUIRE_NONNULL( | ||
maybeFunctor, "Attempted to invoke CaptureForCoroutine functor multiple times")); | ||
KJ_IREQUIRE(maybeFunctor != nullptr, | ||
"Attempted to invoke CaptureForCoroutine functor multiple times"); | ||
auto localFunctor = kj::mv(*kj::_::readMaybe(maybeFunctor)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why not KJ_IF_MAYBE? Can't you write the same code using it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's just a microoptimization that the compiler could also probably figure out. If I used a KJ_IF_MAYBE, I would be adding a redundant nullness check. |
||
maybeFunctor = nullptr; | ||
return coInvoke(kj::mv(localFunctor), kj::fwd<Args>(args)...); | ||
} | ||
}; | ||
|
||
} // namespace _ | ||
|
||
template <typename Functor> | ||
auto coCapture(Functor&& f) { | ||
// Wrap a functor with a lambda that stores it on the heap, invokes it, and then attaches the | ||
// functor to the result promise. | ||
// Assuming `f()` returns a Promise<T> `p`, wrap `f` in such a way that it will outlive its | ||
// returned Promise. Note that the returned object may only be invoked once. | ||
// | ||
// This function is meant to help address this pain point with functors that return a coroutine: | ||
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#cp51-do-not-use-capturing-lambdas-that-are-coroutines | ||
// https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rcoro-capture | ||
// | ||
// The two most common patterns where this may be useful look like so: | ||
// ``` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the name functor here confuses me a lot.
As far as I remember the theory, in expression
map(list, f)
-map
is a functor.f
is a function.Or more exactly:
map(*, f)
is a list->list functor. (https://en.wikipedia.org/wiki/Functor_(functional_programming)#:~:text=In%20functional%20programming%2C%20a%20functor,in%20Haskell%20using%20type%20class)So in this context CaptureForCoroutine is a functor. And it operates on functions.
Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with you, but the misnomer is already quite prevalent in this codebase. I refrained from changing the names (I prefer "function object", or maybe nowadays "invokable", myself) to avoid opening the can of worms. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a misnomer, but it's a broader C++ community misnomer, not one unique to this code. That wiki page itself hints at why it ended up getting used here:
I don't think anyone would be too upset if you just renamed this to
fn
though.