class resumption
July 12, 2022 ยท View on GitHub
A resumption represents a suspended computation. A resumption is given to the user either as an argument of handler<...>::handle_command, or can be lifted from a function using a constructor or wrap.
template <typename T>
class resumption;
template <typename Out, typename Answer>
class resumption<Answer(Out)> {
public:
resumption();
resumption(resumption_data<Out, Answer>* data);
resumption(std::function<Answer(Out)>);
resumption(const resumption<Answer(Out)>&) = delete;
resumption(resumption<Answer(Out)>&& other);
resumption& operator=(const resumption<Answer(Out)>&) = delete;
resumption& operator=(resumption<Answer(Out)>&& other);
~resumption();
explicit operator bool() const;
bool operator!() const;
resumption_data<Out, Answer>* release();
Answer resume(Out cmdResult) &&;
Answer tail_resume(Out cmdResult) &&;
};
template <typename Answer>
class resumption<Answer()> {
public:
resumption();
resumption(resumption_data<void, Answer>* data);
resumption(std::function<Answer()>);
resumption(const resumption<Answer()>&) = delete;
resumption(resumption<Answer()>&& other);
resumption& operator=(const resumption<Answer()>&) = delete;
resumption& operator=(resumption<Answer()>&& other);
~resumption();
explicit operator bool() const;
bool operator!() const;
resumption_data<void, Answer>* release();
Answer resume() &&;
Answer tail_resume() &&;
};
Objects of the resumption class are movable but not copyable. This is because they represent suspended one-shot continuations.
The resumption class is actually a form of a smart pointer, so moving it around is cheap.
Type parameters:
typename T- A function type that corresponds to the type of the suspended computation.
Specialisations:
-
resumption<Answer()>- A computations that, when resumed, will return a value of typeAnswer. -
resumption<Answer(Out)>- A computation that needs a value of typeOutto be resumed (Outis usually the output type of the operation on which the computation is suspended), and will return a value of typeAnswer.
:large_orange_diamond: resumption::resumption
/* 1 */ resumption<Answer(Out)>::resumption()
/* 2 */ resumption<Answer(Out)>::resumption(resumption_data<Out, Answer>* data)
/* 3 */ resumption<Answer(Out)>::resumption(std::function<Answer(Out)> func)
/* 4 */ resumption<Answer()>::resumption(std::function<Answer()> func)
Constructors.
-
1- Create a trivial invalid resumption. -
2- Create a resumption from data previously released withrelease. -
3- Lift a function to a resumption. -
4- As above, specialisation forT Answer().
Arguments:
-
resumption_data<Out, Answer>* data- Data previously released withrelease. -
std::function<Answer(Out)> func- The lifted function. -
std::function<Answer()> func- The lifted function (specialisation forT == Answer()).
:large_orange_diamond: resumption::operator bool
Check if the resumption is valid. The resumption becomes invalid if moved elsewhere (in particular, when resumed).
explicit operator bool() const;
- return value
bool- Indicates if the resumption is valid.
:large_orange_diamond: resumption::operator!
Check if the resumption is invalid. The resumption becomes invalid if moved elsewhere (in particular, when resumed).
bool operator!() const;
- return value
bool- Indicates if the resumption is invalid.
:large_orange_diamond: resumption::release
resumption_data<Out, Answer>* release();
releases the pointer to the suspended computation.
- return value
resumption_data<Out, Answer>*- The released pointer.
Warning: :warning: Never use delete on the released pointer! If you want to get rid of it safely, wrap it back in a dummy resumption value, and let its destructor do the job. For example:
void foo(resumption<int()> r)
{
auto ptr = r.release();
// ...
resumption<int()>{ptr};
}
:large_orange_diamond: resumption::resume
Answer resumption<Answer(Out)>::resume(Out cmdResult) &&
Answer resumption<Answer()>::resume() &&
resume the suspended computation captured in the resumption.
-
Out cmdResult- The value that is returned by the command on which the resumption "hangs". -
Return value
Answer- The result of the resumed computation.
:large_orange_diamond: resumption::tail_resume
Answer resumption<Answer(Out)>::tail_resume(Out cmdResult) &&
Answer resumption<Answer()>::tail_resume() &&
Use to resume the suspended computation captured in the resumption in a tail position in the command clause. This is to be used only inside a command clause as the returned expression. Semantically, for an rvalue reference r, the expressions return r.resume(...); and return r.tail_resume(...); are semantically equivalent, but the latter does not build up the call stack.
-
Out cmdResult- The value that is returned by the command on which the resumption "hangs". -
Return value
Answer- The result of the resumed computation.
Tail-resumes are useful in command clauses such as:
int handle_command(SomeCommand, resumption<int()> r) override
{
// do sth
return std::move(r).tail_resume();
}
In this example, we will build up the call stack until the entire handler returns a final answer (resulting in a "stack leak"). The library has a separate trampolining mechanism built in to avoid this:
// do sth
return std::move(r).tail_resume();
NOTE: tail_resume can be used only if Answer is trivially constructible. Consider the following command clause:
class H : Handler<Answer, void, Op> {
// ...
Answer handle_command(Op, resumption<Answer()> r) override
{
return std::move(r).tail_resume();
}
}
What happens behind the scenes is that tail_resume returns a trivial value of type Answer, while the real resuming happens in a trampoline hidden in the handle function.