Вопрос

Я сомневаюсь, что это можно сделать портативно, но есть ли какие-нибудь решения?Я думаю, что это можно сделать, создав альтернативный стек и сбросив SP, BP и IP при входе в функцию, а также сохранив IP-адрес и восстановив SP+BP.Деструкторы и безопасность исключений кажутся сложными, но решаемыми задачами.

Это было сделано?Это невозможно?

Это было полезно?

Решение

Да, это может быть сделано без проблем.Все, что вам нужно, — это небольшой ассемблерный код для перемещения стека вызовов в новый стек в куче.

Я бы посмотри на boost::сопрограмма библиотека.

Единственное, на что вам следует обратить внимание, — это переполнение стека.В большинстве операционных систем переполнение стека приведет к сбою сегмента, поскольку страница виртуальной памяти не отображается.Однако если вы разместите стек в куче, вы не получите никакой гарантии.Просто имейте это в виду.

Другие советы

В POSIX вы можете использовать процедуры makecontext()/swapcontext() для переносимого переключения контекстов выполнения.В Windows вы можете использовать API волокна.В противном случае все, что вам нужно, — это небольшой ассемблерный код, который переключает контекст машины.Я реализовал сопрограммы как с помощью ASM (для AMD64), так и с помощью swapcontext();это не очень сложно.

Для потомков,

Дмитрий Вьюков замечательный веб-сайт есть хитрый трюк с использованием ucontext и setjump для имитации сопрограмм в C++.

Кроме того, была использована контекстная библиотека Оливера Ковальке. недавно принят в Boost, так что, надеюсь, скоро мы увидим обновленную версию boost.coroutine, которая работает на x86_64.

Не существует простого способа реализовать сопрограмму.Потому что сама сопрограмма, как и поток, не принадлежит к абстракции стека C/C++.Поэтому его невозможно поддерживать без изменений уровня языка для поддержки.

В настоящее время (C++11) все существующие реализации сопрограмм C++ основаны на хакерском уровне сборки, который трудно обеспечить безопасным и надежным при пересечении платформ.Чтобы быть надежным, он должен быть стандартным и обрабатываться компиляторами, а не хакерами.

Есть стандартное предложение - N3708 для этого.Посмотрите, если вам интересно.

Возможно, вам лучше использовать итератор, чем сопрограмму, если это возможно.Таким образом, вы можете продолжать звонить next() чтобы получить следующее значение, но вы можете сохранить свое состояние как переменные-члены вместо локальных переменных.

Это может сделать вещи более удобными в обслуживании.Другой разработчик C++ может не сразу понять сопрограмму, тогда как он может быть более знаком с итератором.

Для тех, кто хочет знать, как можно использовать сопрограммы переносимым способом в C++ y̶o̶u̶ ̶w̶i̶l̶l̶ ̶h̶a̶v̶e̶ ̶t̶o̶ ̶w̶a̶i̶t̶ ̶f̶o̶r̶ ̶C̶+̶+̶1̶7̶, ожидание окончено (см. ниже)!Комитет по стандартизации работает над этой функцией, см. Бумага N3722.Подводя итог текущему проекту статьи, вместо Async и Await будут использоваться ключевые слова resumable и await.

Взгляните на экспериментальную реализацию в Visual Studio 2015 и поэкспериментируйте с экспериментальной реализацией Microsoft.Не похоже, что у clang еще есть реализация.

Есть хороший доклад от Cppcon Сопрограммы — абстракция с отрицательными накладными расходами. опишите преимущества использования сопрограмм в C++ и то, как это влияет на простоту и производительность кода.

В настоящее время нам все еще приходится использовать реализации библиотек, но в ближайшем будущем сопрограммы станут основной функцией C++.

Обновлять:Похоже, реализация сопрограммы запланирована для C++20, но была выпущена как техническая спецификация с C++17 (p0057r2).Visual C++, clang и gcc позволяют вам использовать флаг времени компиляции.

Делает COROUTINE — портативная библиотека C++ для секвенирования сопрограмм. указать вам правильное направление?Кажется, это элегантное решение, выдержавшее испытание временем… ему уже 9 лет!

В папке DOC находится PDF-файл с документом Келда Хелсгауна «Переносимая библиотека C++ для секвенирования сопрограмм», в котором описывается библиотека и приводятся краткие примеры ее использования.

[обновление] На самом деле я сам успешно использую это.Любопытство взяло надо мной верх, поэтому я изучил это решение и обнаружил, что оно хорошо подходит для проблемы, над которой я работал уже некоторое время!

Я не думаю, что на C++ существует много полноценных и чистых реализаций.Одна попытка, которая мне нравится, это Библиотека протопотоков Адама Данкелса.

Смотрите также Протопотоки:упрощение событийно-ориентированного программирования встраиваемых систем с ограниченной памятью в цифровой библиотеке ACM и обсуждение в теме Википедии Протопоток,

Новая библиотека, Boost.Контекст, был выпущен сегодня с портативными функциями для реализации сопрограмм.

Это старая тема, но я хотел бы предложить хак с использованием устройства Даффа, который не зависит от операционной системы (насколько я помню):

Сопрограммы C с использованием устройства Даффа

И в качестве примера, вот библиотека telnet, которую я модифицировал, чтобы использовать сопрограммы вместо fork/threads:Библиотека Telnet cli с использованием сопрограмм

А поскольку стандарт C, предшествующий C99, по существу является подмножеством C++, это хорошо работает и в C++.

Он основан на (съеживающихся) макросах, но на следующем сайте представлена ​​простая в использовании реализация генератора: http://www.codeproject.com/KB/cpp/cpp_generators.aspx

Я придумал реализацию без асм код.Идея состоит в том, чтобы использовать функцию создания системного потока для инициализации стека и контекста и использовать setjmp/longjmp для переключения контекста.Но он не портативный, см. сложная версия pthread если ты заинтересован.

https://github.com/tonbit/coroutine — это реализация единой асимметричной сопрограммы C++11 .h, поддерживающая примитивы возобновления/выхода/ожидания и модель канала.Он реализуется через ucontext/fiber, независимо от повышения, работает на Linux/Windows/macOS.Это хорошая отправная точка для изучения реализации сопрограммы на C++.

Посмотрите мою реализацию, она иллюстрирует точку взлома asm и проста:

https://github.com/user1095108/generic/blob/master/coroutine.hpp

Вместо этого вам всегда следует рассмотреть возможность использования потоков;особенно в современном оборудовании.Если у вас есть работа, которую можно логически разделить в сопрограммах, использование потоков означает, что работа фактически может выполняться одновременно отдельными исполнительными блоками (ядрами процессора).

Но, возможно, вы хотите использовать сопрограммы, возможно, потому, что у вас есть хорошо протестированный алгоритм, который уже был написан и протестирован таким образом, или потому, что вы портируете код, написанный таким образом.

Если вы работаете в Windows, вам следует взглянуть на волокна.Fibers предоставит вам структуру, подобную сопрограмме, с поддержкой ОС.

Я не знаком с другими ОС, чтобы рекомендовать им альтернативы.

ВвКонт является частью WvStreams который реализует так называемые полусопрограммы.С ними немного проще обращаться, чем с полноценными сопрограммами:вы вызываете его, и он возвращается к человеку, который его вызвал.

Это реализовано с использованием более гибкого WvTask, который поддерживает полноценные сопрограммы;вы можете найти его в той же библиотеке.

Работает, по крайней мере, на Win32 и Linux и, возможно, на любой другой системе Unix.

Я сам пытался реализовать сопрограммы, используя C++11 и потоки:

#include <iostream>
#include <thread>

class InterruptedException : public std::exception {
};

class AsyncThread {
public:
    AsyncThread() {
        std::unique_lock<std::mutex> lock(mutex);
        thread.reset(new std::thread(std::bind(&AsyncThread::run, this)));
        conditionVar.wait(lock); // wait for the thread to start
    }
    ~AsyncThread() {
        {
            std::lock_guard<std::mutex> _(mutex);
            quit = true;
        }
        conditionVar.notify_all();
        thread->join();
    }
    void run() {
        try {
            yield();
            for (int i = 0; i < 7; ++i) {
                std::cout << i << std::endl;
                yield();
            }
        } catch (InterruptedException& e) {
            return;
        }
        std::lock_guard<std::mutex> lock(mutex);
        quit = true;
        conditionVar.notify_all();
    }
    void yield() {
        std::unique_lock<std::mutex> lock(mutex);
        conditionVar.notify_all();
        conditionVar.wait(lock);
        if (quit) {
            throw InterruptedException();
        }
    }
    void step() {
        std::unique_lock<std::mutex> lock(mutex);
        if (!quit) {
            conditionVar.notify_all();
            conditionVar.wait(lock);
        }
    }
private:
    std::unique_ptr<std::thread> thread;
    std::condition_variable conditionVar;
    std::mutex mutex;
    bool quit = false;
};

int main() {
    AsyncThread asyncThread;
    for (int i = 0; i < 3; ++i) {
        std::cout << "main: " << i << std::endl;
        asyncThread.step();
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top