Pergunta

Eu duvido que isso pode ser feito portably, mas existem soluções lá fora? Eu acho que isso poderia ser feito através da criação de uma pilha alternativo e zerando SP, BP e IP na entrada da função, e ter rendimento save IP e restaurar SP + BP. Destruidores e segurança exceção parecer complicado, mas solucionável.

Tem sido feito? É impossível?

Foi útil?

Solução

Sim, pode ser feito sem nenhum problema. Tudo que você precisa é de um pouco de código de montagem para mover a pilha de chamadas para uma pilha recém-alocado no heap.

Eu faria olhada na boost :: biblioteca coroutine .

A única coisa que você deve observar é um estouro de pilha. Na maioria dos sistemas operacionais que transbordam a pilha irá causar um segfault porque página de memória virtual não está mapeado. No entanto, se você alocar a pilha na pilha você não obter qualquer garantia. Basta manter isso em mente.

Outras dicas

Em POSIX, você pode usar makecontext () / swapcontext () rotinas para contextos de execução interruptor portably. No Windows, você pode usar a API de fibra. Caso contrário, basta-lhe um bit de código do conjunto cola que muda o contexto da máquina. Eu tenho implementado coroutines ambos com ASM (para AMD64) e com swapcontext (); nem é muito difícil.

Para a posteridade,

O Dmitry Vyukov wondeful web site tem um truque inteligente usando ucontext e setjump para coroutines simulados em c ++.

Além disso, biblioteca contexto de Oliver Kowalke foi href="http://lists.boost.org/boost-announce/2012/01/0348.php" recentemente aceitou em boost, por isso espero que nós estaremos vendo uma versão atualizada do boost.coroutine que funciona em x86_64 em breve.

Não há nenhuma maneira fácil de implementar co-rotina. Porque a própria co-rotina está fora de C / C ++ 's pilha abstração apenas como fio. Portanto, não pode ser suportado sem alterações nível de linguagem para apoio.

Atualmente (C ++ 11), todos os existentes C ++ implementações coroutine são todos baseados em pirataria montagem de nível que é difícil de ser travessia segura e confiável sobre as plataformas. Para ser confiável, ele precisa ser standard, e manipulados por compiladores, em vez de hacking.

Há um proposta padrão - N3708 para isso. Confira se você estiver interessado.

Você pode ser melhor fora com um iterador que uma co-rotina, se possível. Dessa forma, você pode manter chamando next() para obter o próximo valor, mas você pode manter o seu estado como variáveis ??de membro em vez de variáveis ??locais.

Pode tornar as coisas mais sustentável. Outro desenvolvedor C ++ pode não entender imediatamente a co-rotina enquanto eles podem estar mais familiarizado com um iterador.

Para aqueles que querem saber como eles podem alavancar co-rotinas de uma forma portátil em C ++ você vai ter que esperar para C + + ? 17 a espera acabou (veja abaixo)! O comitê de padrões está trabalhando no recurso de ver a N3722 papel . Para resumir o actual projecto do papel, em vez de Async e aguardar, as palavras-chave será resumable, e aguardar.

Dê uma olhada na implementação experimental em Visual Studio 2015 para jogar com a implementação experimental da Microsoft. Ele não se parece com clang tem uma implementação ainda.

Há uma boa conversa a partir Cppcon coroutines uma abstração negativa sobrecarga delinear os benefícios da utilização de co-rotinas em C ++ e como isso afeta a simplicidade eo desempenho do código.

No momento ainda temos que usar implementações de biblioteca, mas em um futuro próximo, teremos coroutines como um núcleo C ++ recurso.

Update: Parece que a implementação co-rotina está previsto para C ++ 20, mas foi lançado como uma especificação técnica com C ++ 17 ( p0057r2 ). Visual C ++, clang e gcc permitem que você optar em usar uma bandeira tempo de compilação.

A biblioteca coroutine um portátil C ++ para o seqüenciamento coroutine apontá-lo na direção certa? Parece uma solução elegante que durou o teste do tempo ..... é de 9 anos!

Na pasta DOC é um pdf do papel A Biblioteca portátil C ++ para coroutine Sequencing por Keld Helsgaun que descreve a biblioteca e fornece exemplos curtos de usá-lo.

[update] Eu estou realmente fazendo uso bem sucedido do que eu. A curiosidade levou o melhor de mim, então eu olhei para esta solução, e achei que era um bom ajuste para um problema que eu tenho vindo a trabalhar há algum tempo!

Eu não acho que há muitos full-blown, implementações limpas em C ++. Uma tentativa que eu gosto é Adam Dunkels' protothread biblioteca .

Veja também Protothreads: simplificando programação orientada a eventos de sistemas embarcados com restrição de memória na Biblioteca digital ACM e discussão no tópico Wikipedia Protothread ,

A nova biblioteca, impulso .context , foi lançado hoje com características portáteis para implementar co-rotinas.

Esta é uma discussão antiga, mas eu gostaria de sugerir um hack usando o dispositivo de Duff que não os-dependente (tanto quanto me lembro):

C coroutines usando o dispositivo de Duff

E como exemplo, aqui é uma biblioteca de telnet I modificado para usar co-rotinas em vez de garfo / tópicos: Telnet biblioteca cli usando coroutines

E desde padrão C antes do C99 é, essencialmente, um verdadeiro subconjunto de C ++, isso funciona bem em C ++ também.

É baseado em (cringe) macros, mas o seguinte site fornece uma implementação gerador easy-to-use: http://www.codeproject.com/KB/cpp/cpp_generators.aspx

Eu vim com uma implementação sem asm código. A idéia é usar função thread criar o sistema de pilha de inicialização e contexto, e uso setjmp / longjmp ao contexto switch. Mas não é portátil, consulte a pthread complicado versão se você estiver interessado.

https://github.com/tonbit/coroutine é C ++ 11 individuais .h coroutine aplicação assimétrica de suporte retomar / rendimento / primitivas e aguardam modelo de canal. É implementar via ucontext / fibra, não dependendo de impulso, rodando em Linux / Windows / MacOS. É um bom ponto de partida para aprender a implementação co-rotina em C ++.

Confira minha implementação, ilustra o ponto de hackers asm e é simples:

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

Você deve sempre considerar o uso de tópicos em vez; especialmente em hardware moderno. Se você tem um trabalho que pode ser logicamente separados em co-rotinas, usando tópicos significa que o trabalho pode realmente ser feito simultaneamente, por unidades de execução separados (núcleos de processador).

Mas, talvez você queira usar coroutines, talvez porque você tem um algoritmo bem testado que já foi escrito e testado dessa maneira, ou porque você está portando o código escrito dessa forma.

Se você trabalha dentro do Windows, você deve dar uma olhada fibras . Fibras lhe dará uma coroutine-como quadro com o apoio da OS.

Eu não estou familiarizado com outro sistema operacional de recomendar alternativas lá.

WvCont é uma parte da WvStreams que implementa os chamados semi-coroutines . Estes são um pouco mais fácil de lidar do que full-on coroutines: você ligar para ele, e ele produz de volta para a pessoa que o chamou

.

Ele é implementado utilizando o WvTask mais flexível, que suporta full-on coroutines; você pode encontrá-lo na mesma biblioteca.

Obras no Win32 e Linux, pelo menos, e, provavelmente, qualquer outro sistema Unix.

Eu tentei implementar co-rotinas me usando C ++ 11 e tópicos:

#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();
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top