mirror of https://gitee.com/bigwinds/arangodb
665 lines
25 KiB
Plaintext
665 lines
25 KiB
Plaintext
[/
|
|
Copyright Oliver Kowalke 2014.
|
|
Distributed under the Boost Software License, Version 1.0.
|
|
(See accompanying file LICENSE_1_0.txt or copy at
|
|
http://www.boost.org/LICENSE_1_0.txt
|
|
]
|
|
|
|
[#ecv2]
|
|
[section:ecv2 Class execution_context (version 2)]
|
|
|
|
[note This class is enabled per default.]
|
|
|
|
Class __econtext__ encapsulates context switching and manages the associated
|
|
context' stack (allocation/deallocation).
|
|
|
|
__econtext__ allocates the context stack (using its [link stack
|
|
__stack_allocator__] argument) and creates a control structure on top of it.
|
|
This structure is responsible for managing context' stack. The address of the
|
|
control structure is stored in the first frame of context' stack (e.g. it can
|
|
not directly accessed from within __econtext__). In contrast to __ecv1__ the
|
|
ownership of the control structure is not shared (no member variable to control
|
|
structure in __econtext__). __econtext__ keeps internally a state that is moved
|
|
by a call of __ec_op__ (`*this` will be invalidated), e.g. after a calling
|
|
__ec_op__, `*this` can not be used for an additional context switch.
|
|
|
|
__econtext__ is only move-constructible and move-assignable.
|
|
|
|
The moved state is assigned to a new instance of __econtext__. This object
|
|
becomes the first argument of the context-function, if the context was resumed
|
|
the first time, or the first element in a tuple returned by __ec_op__ that has
|
|
been called in the resumed context.
|
|
In contrast to __ecv1__, the context switch is faster because no global pointer
|
|
etc. is involved.
|
|
|
|
[important Segmented stacks are not supported by __econtext__ (v2).]
|
|
|
|
On return the context-function of the current context has to specify an
|
|
__econtext__ to which the execution control is transferred after termination
|
|
of the current context.
|
|
|
|
If an instance with valid state goes out of scope and the context-function has
|
|
not yet returned, the stack is traversed in order to access the control
|
|
structure (address stored at the first stack frame) and context' stack is
|
|
deallocated via the __stack_allocator__. The stack walking makes the destruction
|
|
of __econtext__ slow and should be prevented if possible.
|
|
|
|
__econtext__ expects a __context_fn__ with signature
|
|
`execution_context(execution_context ctx, Args ... args)`. The parameter `ctx`
|
|
represents the context from which this context was resumed (e.g. that has called
|
|
__ec_op__ on `*this`) and `args` are the data passed to __ec_op__. The return
|
|
value represents the execution_context that has to be resumed, after termiantion
|
|
of this context.
|
|
|
|
Benefits of __ecv2__ over __ecv1__ are: faster context switch, type-safety of
|
|
passed/returned arguments.
|
|
|
|
|
|
[heading usage of __econtext__]
|
|
|
|
int n=35;
|
|
ctx::execution_context<int> source(
|
|
[n](ctx::execution_context<int> sink, int) mutable {
|
|
int a=0;
|
|
int b=1;
|
|
while(n-->0){
|
|
auto result=sink(a);
|
|
sink=std::move(std::get<0>(result));
|
|
auto next=a+b;
|
|
a=b;
|
|
b=next;
|
|
}
|
|
return sink;
|
|
});
|
|
for(int i=0;i<10;++i){
|
|
auto result=source(i);
|
|
source=std::move(std::get<0>(result));
|
|
std::cout<<std::get<1>(result)<<" ";
|
|
}
|
|
|
|
output:
|
|
0 1 1 2 3 5 8 13 21 34
|
|
|
|
This simple example demonstrates the basic usage of __econtext__ as a generator.
|
|
The context `sink` represents the ['main]-context (function ['main()] running).
|
|
`sink` is generated by the framework (first element of lambda's parameter list).
|
|
Because the state is invalidated (== changed) by each call of __ec_op__, the new
|
|
state of the __econtext__, returned by __ec_op__, needs to be assigned to `sink`
|
|
after each call.
|
|
|
|
The lambda that calculates the Fibonacci numbers is executed inside
|
|
the context represented by `source`. Calculated Fibonacci numbers are
|
|
transferred between the two context' via expression ['sink(a)] (and returned by
|
|
['source()]). Note that this example represents a ['generator] thus the value
|
|
transferred into the lambda via ['source()] is not used. Using
|
|
['boost::optional<>] as transferred type, might also appropriate to express this
|
|
fact.
|
|
|
|
The locale variables `a`, `b` and ` next` remain their values during each
|
|
context switch (['yield(a)]). This is possible due `source` has its own stack
|
|
and the stack is exchanged by each context switch.
|
|
|
|
|
|
[heading parameter passing]
|
|
With `execution_context<void>` no data will be transferred, only the context
|
|
switch is executed.
|
|
|
|
boost::context::execution_context<void> ctx1([](boost::context::execution_context<void> ctx2){
|
|
std::printf("inside ctx1\n");
|
|
return ctx2();
|
|
});
|
|
ctx1();
|
|
|
|
output:
|
|
inside ctx1
|
|
|
|
`ctx1()` resumes `ctx1`, e.g. the lambda passed at the constructor of `ctx1` is
|
|
entered. Argument `ctx2` represents the context that has been suspended with the
|
|
invocation of `ctx1()`. When the lambda returns `ctx2`, context `ctx1` will be
|
|
terminated while the context represented by `ctx2` is resumed, hence the control
|
|
of execution returns from `ctx1()`.
|
|
|
|
The arguments passed to __ec_op__, in one context, is passed as the last
|
|
arguments of the __context_fn__ if the context is started for the first time.
|
|
In all following invocations of __ec_op__ the arguments passed to __ec_op__, in
|
|
one context, is returned by __ec_op__ in the other context.
|
|
|
|
boost::context::execution_context<int> ctx1([](boost::context::execution_context<int> ctx2, int j){
|
|
std::printf("inside ctx1, j == %d\n", j);
|
|
return ctx2(j+1);
|
|
});
|
|
int i = 1;
|
|
std::tie(ctx1, i) = ctx1(i);
|
|
std::printf("i == %d\n", i);
|
|
|
|
output:
|
|
inside ctx1, j == 1
|
|
i == 2
|
|
|
|
`ctx1(i)` enters the lambda in context `ctx1` with argument `j=1`. The
|
|
expression `ctx2(j+1)` resumes the context represented by `ctx2` and transfers
|
|
back an integer of `j+1`. On return of `ctx1(i)`, the variable `i` contains the
|
|
value of `j+1`.
|
|
|
|
If more than one argument has to be transferred, the signature of the
|
|
context-function is simply extended.
|
|
|
|
boost::context::execution_context<int,int> ctx1([](boost::context::execution_context<int,int> ctx2, int i, int j){
|
|
std::printf("inside ctx1, i == %d j == %d\n", i, j);
|
|
return ctx2(i+j,i-j);
|
|
});
|
|
int i = 2, j = 1;
|
|
std::tie(ctx1, i, j) = ctx1(i,j);
|
|
std::printf("i == %d j == %d\n", i, j);
|
|
|
|
output:
|
|
inside ctx1, i == 2 j == 1
|
|
i == 3 j == 1
|
|
|
|
For use-cases, that require to transfer data of different type in each
|
|
direction, ['boost::variant<>] could be used.
|
|
|
|
class X{
|
|
private:
|
|
std::exception_ptr excptr_;
|
|
boost::context::execution_context<boost::variant<int,std::string>> ctx_;
|
|
|
|
public:
|
|
X():
|
|
excptr_(),
|
|
ctx_(
|
|
[=](boost::context::execution_context<boost::variant<int,std::string>> ctx, boost::variant<int,std::string> data){
|
|
try {
|
|
for (;;) {
|
|
int i = boost::get<int>(data);
|
|
data = boost::lexical_cast<std::string>(i);
|
|
auto result = ctx( data);
|
|
ctx = std::move( std::get<0>( result) );
|
|
data = std::get<1>( result);
|
|
} catch (std::bad_cast const&) {
|
|
excptr_=std::current_exception();
|
|
}
|
|
return ctx;
|
|
})
|
|
{}
|
|
|
|
std::string operator()(int i){
|
|
boost::variant<int,std::string> data = i;
|
|
auto result = ctx_( data);
|
|
ctx_ = std::move( std::get<0>( result) );
|
|
data = std::get<1>( result);
|
|
if(excptr_){
|
|
std::rethrow_exception(excptr_);
|
|
}
|
|
return boost::get<std::string>(data);
|
|
}
|
|
};
|
|
|
|
X x;
|
|
std::cout << x( 7) << std::endl;
|
|
|
|
output:
|
|
7
|
|
|
|
In the case of unidirectional transfer of data, ['boost::optional<>] or a
|
|
pointer are appropriate.
|
|
|
|
|
|
[heading exception handling]
|
|
If the function executed inside a __econtext__ emits ans exception, the
|
|
application is terminated by calling ['std::terminate()]. ['std::exception_ptr]
|
|
can be used to transfer exceptions between different execution contexts.
|
|
|
|
[important Do not jump from inside a catch block and then re-throw the exception
|
|
in another execution context.]
|
|
|
|
[#ecv2_ontop]
|
|
[heading Executing function on top of a context]
|
|
Sometimes it is useful to execute a new function on top of a resumed context.
|
|
For this purpose __ec_op__ with first argument `exec_ontop_arg` has to be used.
|
|
The function passed as argument must return a tuple of execution_context and
|
|
arguments.
|
|
|
|
boost::context::execution_context<int> f1(boost::context::execution_context<int> ctx,int data) {
|
|
std::cout << "f1: entered first time: " << data << std::endl;
|
|
std::tie(ctx,data) = ctx(data+1);
|
|
std::cout << "f1: entered second time: " << data << std::endl;
|
|
std::tie(ctx,data) = ctx(data+1);
|
|
std::cout << "f1: entered third time: " << data << std::endl;
|
|
return ctx;
|
|
}
|
|
|
|
std::tuple<boost::context::execution_context<int>,int> f2(boost::context::execution_context<int> ctx,int data) {
|
|
std::cout << "f2: entered: " << data << std::endl;
|
|
return std::make_tuple(std::move(ctx),-1);
|
|
}
|
|
|
|
int data = 0;
|
|
ctx::execution_context< int > ctx(f1);
|
|
std::tie(ctx,data) = ctx(data+1);
|
|
std::cout << "f1: returned first time: " << data << std::endl;
|
|
std::tie(ctx,data) = ctx(data+1);
|
|
std::cout << "f1: returned second time: " << data << std::endl;
|
|
std::tie(ctx,data) = ctx(ctx::exec_ontop_arg,f2,data+1);
|
|
|
|
output:
|
|
f1: entered first time: 1
|
|
f1: returned first time: 2
|
|
f1: entered second time: 3
|
|
f1: returned second time: 4
|
|
f2: entered: 5
|
|
f1: entered third time: -1
|
|
|
|
The expression `ctx(ctx::exec_ontop_arg,f2,data+1)` executes `f2()` on top of
|
|
context `ctx`, e.g. an additional stack frame is allocated on top of the context
|
|
stack (in front of `f1()`). `f2()` returns argument `-1` that will returned by
|
|
the second invocation of `ctx(data+1)` in `f1()`.
|
|
|
|
Another option is to execute a function on top of the context that throws an
|
|
exception.
|
|
|
|
struct interrupt {
|
|
boost::context::execution_context< void > ctx;
|
|
|
|
interrupt( boost::context::execution_context< void > && ctx_) :
|
|
ctx( std::forward< boost::context::execution_context< void > >( ctx_) ) {
|
|
}
|
|
};
|
|
|
|
boost::context::execution_context<void> f1(boost::context::execution_context<void> ctx) {
|
|
try {
|
|
for (;;) {
|
|
std::cout << "f1()" << std::endl;
|
|
ctx = ctx();
|
|
}
|
|
} catch (interrupt & e) {
|
|
std::cout << "f1(): interrupted" << std::endl;
|
|
ctx = std::move( e.ctx);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
boost::context::execution_context<void> f2(boost::context::execution_context<void> ctx) {
|
|
throw interrupt(std::move(ctx));
|
|
return ctx;
|
|
}
|
|
|
|
boost::context::execution_context< void > ctx(f1);
|
|
ctx = ctx();
|
|
ctx = ctx();
|
|
ctx = ctx(boost::context::exec_ontop_arg,f2);
|
|
|
|
output:
|
|
f1()
|
|
f1()
|
|
f1(): interrupted
|
|
|
|
In this example `f2()` is used to interrupt the `for`-loop in `f1()`.
|
|
|
|
|
|
[heading stack unwinding]
|
|
On construction of __econtext__ a stack is allocated.
|
|
If the __context_fn__ returns the stack will be destructed.
|
|
If the __context_fn__ has not yet returned and the destructor of an valid
|
|
__econtext__ instance (e.g. ['execution_context::operator bool()] returns
|
|
`true`) is called, the stack will be destructed too.
|
|
|
|
[important Code executed by __context_fn__ must not prevent the propagation of the
|
|
__forced_unwind__ exception. Absorbing that exception will cause stack
|
|
unwinding to fail. Thus, any code that catches all exceptions must re-throw any
|
|
pending __forced_unwind__ exception.]
|
|
|
|
|
|
[#ecv2_prealloc]
|
|
[heading allocating control structures on top of stack]
|
|
Allocating control structures on top of the stack requires to allocated the
|
|
__stack_context__ and create the control structure with placement new before
|
|
__econtext__ is created.
|
|
[note The user is responsible for destructing the control structure at the top
|
|
of the stack.]
|
|
|
|
// stack-allocator used for (de-)allocating stack
|
|
fixedsize_stack salloc( 4048);
|
|
// allocate stack space
|
|
stack_context sctx( salloc.allocate() );
|
|
// reserve space for control structure on top of the stack
|
|
void * sp = static_cast< char * >( sctx.sp) - sizeof( my_control_structure);
|
|
std::size_t size = sctx.size - sizeof( my_control_structure);
|
|
// placement new creates control structure on reserved space
|
|
my_control_structure * cs = new ( sp) my_control_structure( sp, size, sctx, salloc);
|
|
...
|
|
// destructing the control structure
|
|
cs->~my_control_structure();
|
|
...
|
|
struct my_control_structure {
|
|
// captured context
|
|
execution_context cctx;
|
|
|
|
template< typename StackAllocator >
|
|
my_control_structure( void * sp, std::size_t size, stack_context sctx, StackAllocator salloc) :
|
|
// create captured context
|
|
cctx( std::allocator_arg, preallocated( sp, size, sctx), salloc, entry_func) {
|
|
}
|
|
...
|
|
};
|
|
|
|
|
|
[heading inverting the control flow]
|
|
|
|
/*
|
|
* grammar:
|
|
* P ---> E '\0'
|
|
* E ---> T {('+'|'-') T}
|
|
* T ---> S {('*'|'/') S}
|
|
* S ---> digit | '(' E ')'
|
|
*/
|
|
class Parser{
|
|
// implementation omitted; see examples directory
|
|
};
|
|
|
|
std::istringstream is("1+1");
|
|
bool done=false;
|
|
std::exception_ptr except;
|
|
|
|
// execute parser in new execution context
|
|
boost::context::execution_context<char> source(
|
|
[&is,&done,&except](ctx::execution_context<char> sink,char){
|
|
// create parser with callback function
|
|
Parser p( is,
|
|
[&sink](char ch){
|
|
// resume main execution context
|
|
auto result = sink(ch);
|
|
sink = std::move(std::get<0>(result));
|
|
});
|
|
try {
|
|
// start recursive parsing
|
|
p.run();
|
|
} catch (...) {
|
|
// store other exceptions in exception-pointer
|
|
except = std::current_exception();
|
|
}
|
|
// set termination flag
|
|
done=true;
|
|
// resume main execution context
|
|
return sink;
|
|
});
|
|
|
|
// user-code pulls parsed data from parser
|
|
// invert control flow
|
|
auto result = source('\0');
|
|
source = std::move(std::get<0>(result));
|
|
char c = std::get<1>(result);
|
|
if ( except) {
|
|
std::rethrow_exception(except);
|
|
}
|
|
while( ! done) {
|
|
printf("Parsed: %c\n",c);
|
|
std::tie(source,c) = source('\0');
|
|
if (except) {
|
|
std::rethrow_exception(except);
|
|
}
|
|
}
|
|
|
|
output:
|
|
Parsed: 1
|
|
Parsed: +
|
|
Parsed: 1
|
|
|
|
In this example a recursive descent parser uses a callback to emit a newly
|
|
passed symbol. Using __econtext__ the control flow can be inverted, e.g. the
|
|
user-code pulls parsed symbols from the parser - instead to get pushed from the
|
|
parser (via callback).
|
|
|
|
The data (character) is transferred between the two __econtext__.
|
|
|
|
If the code executed by __econtext__ emits an exception, the application is
|
|
terminated. ['std::exception_ptr] can be used to transfer exceptions between
|
|
different execution contexts.
|
|
|
|
Sometimes it is necessary to unwind the stack of an unfinished context to
|
|
destroy local stack variables so they can release allocated resources (RAII
|
|
pattern). The user is responsible for this task.
|
|
|
|
|
|
[heading Class `execution_context`]
|
|
|
|
struct exec_ontop_arg_t {};
|
|
const exec_ontop_arg_t exec_ontop_arg{};
|
|
|
|
template< typename ... Args >
|
|
class execution_context {
|
|
public:
|
|
template< typename Fn, typename ... Params >
|
|
execution_context( Fn && fn, Params && ... params);
|
|
|
|
template< typename StackAlloc, typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
|
|
|
|
template< typename StackAlloc, typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
|
|
|
|
template< typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, segemented_stack, Fn && fn, Params && ... params) = delete;
|
|
|
|
template< typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, preallocated palloc, segmented, Fn && fn, Params && ... params)= delete;
|
|
|
|
~execution_context();
|
|
|
|
execution_context( execution_context && other) noexcept;
|
|
execution_context & operator=( execution_context && other) noexcept;
|
|
|
|
execution_context( execution_context const& other) noexcept = delete;
|
|
execution_context & operator=( execution_context const& other) noexcept = delete;
|
|
|
|
explicit operator bool() const noexcept;
|
|
bool operator!() const noexcept;
|
|
|
|
std::tuple< execution_context, Args ... > operator()( Args ... args);
|
|
|
|
template< typename Fn >
|
|
std::tuple< execution_context, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args);
|
|
|
|
bool operator==( execution_context const& other) const noexcept;
|
|
|
|
bool operator!=( execution_context const& other) const noexcept;
|
|
|
|
bool operator<( execution_context const& other) const noexcept;
|
|
|
|
bool operator>( execution_context const& other) const noexcept;
|
|
|
|
bool operator<=( execution_context const& other) const noexcept;
|
|
|
|
bool operator>=( execution_context const& other) const noexcept;
|
|
|
|
template< typename charT, class traitsT >
|
|
friend std::basic_ostream< charT, traitsT > &
|
|
operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
|
|
};
|
|
|
|
[constructor_heading ecv2..constructor]
|
|
|
|
template< typename Fn, typename ... Params >
|
|
execution_context( Fn && fn, Params && ... params);
|
|
|
|
template< typename StackAlloc, typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, StackAlloc salloc, Fn && fn, Params && ... params);
|
|
|
|
template< typename StackAlloc, typename Fn, typename ... Params >
|
|
execution_context( std::allocator_arg_t, preallocated palloc, StackAlloc salloc, Fn && fn, Params && ... params);
|
|
|
|
[variablelist
|
|
[[Effects:] [Creates a new execution context and prepares the context to execute
|
|
`fn`. `fixedsize_stack` is used as default stack allocator
|
|
(stack size == fixedsize_stack::traits::default_size()).
|
|
The constructor with argument type `preallocated`, is used to create a user
|
|
defined data [link ecv2_prealloc (for instance additional control structures)] on
|
|
top of the stack.]]
|
|
]
|
|
[destructor_heading ecv2..destructor destructor]
|
|
|
|
~execution_context();
|
|
|
|
[variablelist
|
|
[[Effects:] [Destructs the associated stack if `*this` is a valid context,
|
|
e.g. ['execution_context::operator bool()] returns `true`.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[move_constructor_heading ecv2..move constructor]
|
|
|
|
execution_context( execution_context && other) noexcept;
|
|
|
|
[variablelist
|
|
[[Effects:] [Moves underlying capture record to `*this`.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[move_assignment_heading ecv2..move assignment]
|
|
|
|
execution_context & operator=( execution_context && other) noexcept;
|
|
|
|
[variablelist
|
|
[[Effects:] [Moves the state of `other` to `*this` using move semantics.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_bool..operator bool]
|
|
|
|
explicit operator bool() const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this` points to a capture record.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_not..operator!]
|
|
|
|
bool operator!() const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this` does not point to a capture record.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_call..operator()]
|
|
|
|
std::tuple< execution_context< Args ... >, Args ... > operator()( Args ... args); // member of generic execution_context template
|
|
|
|
execution_context< void > operator()(); // member of execution_context< void >
|
|
|
|
[variablelist
|
|
[[Effects:] [Stores internally the current context data (stack pointer,
|
|
instruction pointer, and CPU registers) of the current active context and
|
|
restores the context data from `*this`, which implies jumping to `*this`'s
|
|
context.
|
|
The arguments, `... args`, are passed to the current context to be returned
|
|
by the most recent call to `execution_context::operator()` in the same thread.]]
|
|
[[Returns:] [The tuple of execution_context and returned arguments passed to the
|
|
most recent call to `execution_context::operator()`, if any and a
|
|
execution_context representing the context that has been suspended.]]
|
|
[[Note:] [The returned execution_context indicates if the suspended context has
|
|
terminated (return from context-function) via `bool operator()`. If the returned
|
|
execution_context has terminated no data are transferred in the returned tuple.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_call_ontop..operator()]
|
|
|
|
template< typename Fn >
|
|
std::tuple< execution_context< Args ... >, Args ... > operator()( exec_ontop_arg_t, Fn && fn, Args ... args); // member of generic execution_context
|
|
|
|
template< typename Fn >
|
|
execution_context< void > operator()( exec_ontop_arg_t, Fn && fn); // member of execution_context< void >
|
|
|
|
[variablelist
|
|
[[Effects:] [Same as __ec_op__. Additionally, function `fn` is executed
|
|
in the context of `*this` (e.g. the stack frame of `fn` is allocated on
|
|
stack of `*this`).]]
|
|
[[Returns:] [The tuple of execution_context and returned arguments passed to the
|
|
most recent call to `execution_context::operator()`, if any and a
|
|
execution_context representing the context that has been suspended .]]
|
|
[[Note:] [The tuple of execution_context and returned arguments from `fn` are
|
|
passed as arguments to the context-function of resumed context (if the context
|
|
is entered the first time) or those arguments are returned from
|
|
`execution_context::operator()` within the resumed context.]]
|
|
[[Note:] [Function `fn` needs to return a tuple of execution_context and
|
|
arguments ([link ecv2_ontop see description]).]]
|
|
[[Note:] [The context calling this function must not be destroyed before the
|
|
arguments, that will be returned from `fn`, are preserved at least in the stack
|
|
frame of the resumed context.]]
|
|
[[Note:] [The returned execution_context indicates if the suspended context has
|
|
terminated (return from context-function) via `bool operator()`. If the returned
|
|
execution_context has terminated no data are transferred in the returned tuple.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_equal..operator==]
|
|
|
|
bool operator==( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this` and `other` represent the same execution context,
|
|
`false` otherwise.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_notequal..operator!=]
|
|
|
|
bool operator!=( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [[`! (other == * this)]]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_less..operator<]
|
|
|
|
bool operator<( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`true` if `*this != other` is true and the
|
|
implementation-defined total order of `execution_context` values places `*this`
|
|
before `other`, false otherwise.]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_greater..operator>]
|
|
|
|
bool operator>( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`other < * this`]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_lesseq..operator<=]
|
|
|
|
bool operator<=( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`! (other < * this)`]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[operator_heading ecv2..operator_greatereq..operator>=]
|
|
|
|
bool operator>=( execution_context const& other) const noexcept;
|
|
|
|
[variablelist
|
|
[[Returns:] [`! (* this < other)`]]
|
|
[[Throws:] [Nothing.]]
|
|
]
|
|
|
|
[hding ecv2_..Non-member function [`operator<<()]]
|
|
|
|
template< typename charT, class traitsT >
|
|
std::basic_ostream< charT, traitsT > &
|
|
operator<<( std::basic_ostream< charT, traitsT > & os, execution_context const& other);
|
|
|
|
[variablelist
|
|
[[Efects:] [Writes the representation of `other` to stream `os`.]]
|
|
[[Returns:] [`os`]]
|
|
]
|
|
|
|
|
|
[endsect]
|