Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Class execution_context (version 2)

Boost , Chapter 1. Context , Chapter 1. Context

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext
[Note] Note

Этот класс включен по умолчанию.

Классexecution_contextинкапсулирует переключение контекста и управляет стеком связанного контекста (выделение/выделение).

execution_contextвыделяет контекстный стек (используя его)StackAllocatorаргумент и создает поверх него управляющую структуру. Эта структура отвечает за управление стеком контекста. Адрес структуры управления хранится в первом стеке контекста (например, он не может быть напрямую доступен изexecution_context).. В отличие отexecution_context(v1)право собственности на структуру управления не разделено (никакой переменной члена для структуры управления вexecution_context).execution_contextсохраняет внутренне состояние, которое перемещается вызовомexecution_context::operator()(<*this>будет признан недействительным), например, после вызоваexecution_context::operator(),<*this>не может использоваться для дополнительного переключателя контекста.

execution_contextявляется только подвижно-конструктивным и подвижно-назначаемым.

Перемещенное состояние присваивается новому экземпляруexecution_context. Этот объект становится первым аргументом контекстной функции, если контекст был возобновлен в первый раз, или первым элементом в кортеже, возвращеннымexecution_context::operator(), который был вызван в возобновленном контексте. В отличие отexecution_context(v1), переключение контекста происходит быстрее, потому что не задействован глобальный указатель и т.д.

[Important] Important

Сегментированные стеки не поддерживаютсяexecution_context(v2).

По возвращении контекст-функция текущего контекста должна указатьexecution_context, на который передается контроль исполнения после прекращения текущего контекста.

Если экземпляр с действительным состоянием выходит из области действия, а функция контекста еще не вернулась, стек проходит для доступа к структуре управления (адрес, сохраненный в первом кадре стека), и стек контекста распределяется через.StackAllocator. Стековая ходьба делает разрушениеexecution_contextмедленным и должна быть предотвращена, если это возможно.

execution_contextожидаетконтекстную функциюс подписью<execution_context(execution_context ctx,Args...args)>. Параметр<ctx>представляет контекст, из которого этот контекст был возобновлен (например, который назвалexecution_context::operator()on<*this>) и<args>представляют собой данные, переданныеexecution_context::operator(). Значение возврата представляет собой исполняемый_контекст, который должен быть возобновлен после терминации этого контекста.

Преимуществамиexecution_context(v2)по сравнению сexecution_context(v1)являются: более быстрый переключатель контекста, безопасность типа переданных/возвращенных аргументов.

usage of execution_context

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

Этот простой пример демонстрирует базовое использованиеexecution_contextв качестве генератора. Контекст<sink>представляет собойосновной-контекст (функцияосновной()работающий).<sink>генерируется рамкой (первый элемент списка параметров лямбды). Поскольку состояние недействительно (== изменяется) каждым вызовомexecution_context::operator(), новое состояниеexecution_context, возвращаемоеexecution_context::operator(), должно быть назначено<sink>после каждого вызова.

Ламбда, вычисляющая числа Фибоначчи, выполняется внутри контекста, представленного<source>. Вычисленные числа Фибоначчи переносятся между двумя контекстами посредством выраженияsink(a)(и возвращаютсяsource()). Обратите внимание, что этот пример представляет собойгенератор, таким образом, значение, переданное в лямбду черезисточник(), не используется. Использованиебустера::optional<>в качестве перенесенного типа, также может быть уместным для выражения этого факта.

Локальные переменные<a>,<b>и<next>остаются своими значениями в течение каждого контекстного переключателявыход (a). Это возможно, потому что<source>имеет свой собственный стек, и стек обменивается каждым коммутатором контекста.

parameter passing

Если<execution_context<void>>данные не передаются, выполняется только переключатель контекста.

boost::context::execution_context<void> ctx1([](boost::context::execution_context<void> ctx2){
            std::printf("inside ctx1\n");
            return ctx2();
        });
ctx1();
output:
    inside ctx1

<ctx1()>резюме<ctx1>, например, лямбда, пропущенная у конструктора<ctx1>, вводится. Аргумент<ctx2>представляет собой контекст, который был приостановлен с призывом<ctx1()>. Когда лямбда возвращается<ctx2>, контекст<ctx1>будет прекращен, а контекст, представленный<ctx2>, возобновлен, следовательно, контроль исполнения возвращается из<ctx1()>.

Аргументы, переданныеexecution_context::operator(), в одном контексте, передаются в качестве последних аргументовконтекст-функции, если контекст запущен впервые. Во всех следующих вызовахexecution_context::operator()аргументы, переданныеexecution_context::operator(), в одном контексте возвращаютсяexecution_context::operator()в другом контексте.

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)>входит в лямбду в контексте<ctx1>с аргументом<j=1>. Выражение<ctx2(j+1)>возобновляет контекст, представленный<ctx2>, и возвращает целое число<j+1>. При возврате<ctx1(i)>переменная<i>содержит значение<j+1>.

Если необходимо передать более одного аргумента, подпись контекстной функции просто расширяется.

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

Для вариантов использования, которые требуют передачи данных различного типа в каждом направлении, можно использоватьboost::variant<>.

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

В случае однонаправленной передачи данных уместныбустер::optional<>или указатель.

exception handling

Если функция, выполняемая внутриexecution_context, испускает исключение, приложение прекращается вызовомstd::terminate().std:: Exception_ptrможет использоваться для передачи исключений между различными контекстами исполнения.

[Important] Important

Не прыгайте изнутри блок-уловки, а затем повторно бросайте исключение в другой контекст исполнения.

Executing function on top of a context

Иногда полезно выполнять новую функцию поверх возобновленного контекста. Для этой целиexecution_context::operator()с первым аргументом<exec_ontop_arg>следует использовать. Функция, принятая в качестве аргумента, должна возвращать набор из execution_context и аргументов.

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

Выражение<ctx(ctx::exec_ontop_arg,f2,data+1)>выполняет<f2()>поверх контекста<ctx>, например, дополнительная рамка стека выделяется поверх контекстного стека (перед<f1()>).<f2()>возвращает аргумент<-1>, который будет возвращен вторым призывом<ctx(data+1)>в<f1()>.

Другой вариант — выполнить функцию поверх контекста, которая делает исключение.

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

В этом примере<f2()>используется для прерывания<for>-петли в<f1()>.

stack unwinding

На конструкциюexecution_contextвыделен стек. Есликонтекстная функциявозвращается, стек будет уничтожен. Есликонтекстная функцияеще не вернулась и деструктор действительногоэкземпляра execution_context(например,execution_context::operator bool()возвращает<true>) называется, стек также будет уничтожен.

[Important] Important

Код, выполняемыйконтекстной функцией, не должен препятствовать распространениюдетали::forced_unwindисключения. Поглощение этого исключения приведет к провалу стека. Таким образом, любой код, который улавливает все исключения, должен повторно бросить любую ожидающуюдеталь::forced_unwindисключение.

allocating control structures on top of stack

Выделение управляющих структур поверх стека требует выделенияstack_contextи создания управляющей структуры с размещением новой до созданияexecution_context.

[Note] Note

Пользователь несет ответственность за разрушение структуры управления в верхней части стека.

// 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) {
    }
    ...
};

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

В этом примере рекурсивный парсер спуска использует обратный вызов, чтобы излучать недавно принятый символ. Используяexecution_context, поток управления может быть инвертирован, например, пользовательский код вытягивает парсированные символы из парсера - вместо этого его выталкивают из парсера (через обратный вызов).

Данные (характер) передаются между двумяexecution_context.

Если код, выполняемыйexecution_context, испускает исключение, приложение прекращается.std:: Exception_ptrможет использоваться для передачи исключений между различными контекстами исполнения.

Иногда необходимо раскручивать стек незавершенного контекста, чтобы уничтожить локальные переменные стека, чтобы они могли высвобождать выделенные ресурсы (паттерн RAII). Пользователь несет ответственность за эту задачу.

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

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);

Effects:

Создает новый контекст выполнения и подготавливает контекст для выполнения<fn>.<fixedsize_stack>используется в качестве распределителя стека по умолчанию (размер стека == фиксированный размер_стек::traits::default_size()). Конструктор с типом аргумента<preallocated>, используется для создания пользовательских данных(например, дополнительных структур управления)поверх стека.

Destructor

~execution_context();

Effects:

Уничтожает связанный стек, если<*this>является действительным контекстом, напримерexecution_context::operator bool()Returns<true>.

Throws:

Ничего.

Move constructor

execution_context( execution_context && other) noexcept;

Effects:

Перемещает базовый рекорд захвата в<*this>.

Throws:

Ничего.

Move assignment operator

execution_context & operator=( execution_context && other) noexcept;

Effects:

Переносит состояние<other>на<*this>с помощью семантики перемещения.

Throws:

Ничего.

Member function operator bool()

explicit operator bool() const noexcept;

Returns:

<true>, если<*this>указывает на запись захвата.

Throws:

Ничего.

Member function operator!()

bool operator!() const noexcept;

Returns:

<true>, если<*this>не указывает на запись захвата.

Throws:

Ничего.

Member function 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 >

Effects:

Хранит внутри текущие контекстные данные (указатель стека, указатель команд и регистры процессора) текущего активного контекста и восстанавливает контекстные данные из<*this>, что подразумевает переход к контексту<*this>. Аргументы<...args>передаются в текущий контекст, который должен быть возвращен последним призывом к<execution_context::operator()>в той же нити.

Returns:

Кортеж execution_context и возвращенные аргументы перешли к последнему вызову<execution_context::operator()>, если таковой имеется, и execution_context, представляющий контекст, который был приостановлен.

Note:

Возвращенное исполнение_контекст указывает на прекращение приостановленного контекста (возвращение из контекстной функции) через<bool operator()>. Если возвращенное исполнение_контекст прекращено, никакие данные не передаются в возвращенном наборе.

Member function 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 >

Effects:

Аналогичноexecution_context::operator(). Кроме того, функция<fn>выполняется в контексте<*this>(например, кадр стека<fn>выделен на стеке<*this>).

Returns:

Кортеж execution_context и возвращенные аргументы перешли к последнему вызову<execution_context::operator()>, если таковой имеется, и execution_context, представляющий контекст, который был приостановлен.

Note:

Кортеж execution_context и возвращенные аргументы из<fn>передаются в качестве аргументов в контекст-функцию возобновленного контекста (если контекст введен впервые) или эти аргументы возвращаются из<execution_context::operator()>в возобновленном контексте.

Note:

Функция<fn>требует возврата набора execution_context и аргументовсм. описание.

Note:

Контекст, называющий эту функцию, не должен быть уничтожен до того, как аргументы, которые будут возвращены из<fn>, будут сохранены по крайней мере в стековой системе возобновленного контекста.

Note:

Возвращенное исполнение_контекст указывает на прекращение приостановленного контекста (возвращение из контекстной функции) через<bool operator()>. Если возвращенное исполнение_контекст прекращено, никакие данные не передаются в возвращенном наборе.

Member function operator==()

bool operator==( execution_context const& other) const noexcept;

Returns:

<true>, если<*this>и<other>представляют один и тот же контекст исполнения,<false>в противном случае.

Throws:

Ничего.

Member function operator!=()

bool operator!=( execution_context const& other) const noexcept;

Returns:

<! (other == * this)>

Throws:

Ничего.

Member function operator<()

bool operator<( execution_context const& other) const noexcept;

Returns:

<true>, если<*this!=other>истинно и заданный реализацией полный порядок значений<execution_context>помещает<*this>перед<other>, ложно в противном случае.

Throws:

Ничего.

Member function operator>()

bool operator>( execution_context const& other) const noexcept;

Returns:

<other< *this>

Throws:

Ничего.

Member function operator<=()

bool operator<=( execution_context const& other) const noexcept;

Returns:

<!(other< *this)>

Throws:

Ничего.

Member function operator>=()

bool operator>=( execution_context const& other) const noexcept;

Returns:

<!(* this< other)>

Throws:

Ничего.

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);

Efects:

Записывает представление<other>для потоковой передачи<os>.

Returns:

<os>


PrevUpHomeNext

Статья Class execution_context (version 2) раздела Chapter 1. Context Chapter 1. Context может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 1. Context ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-20 07:32:56/0.012612819671631/0