Domanda

Sono stato con rendimenti in molti dei miei programmi Python, e davvero cancella il codice in molti casi. I bloggato su esso ed è una delle pagine popolari del mio sito.

C # anche offerte resa -. È implementato tramite state-keeping nella parte chiamante, fatto attraverso una classe generata automaticamente che mantiene lo stato, le variabili locali della funzione, ecc

Attualmente sto leggendo su C ++ 0x e le sue integrazioni; e leggendo circa l'attuazione di lambda in C ++ 0x, scopro che è stato fatto tramite classi generate automaticamente anche dotato operatore () memorizzare il codice lambda. La domanda naturale formata nella mia mente:? Hanno fatto per lambda, perché non hanno la considerano per il supporto di "resa", troppo

Sicuramente può vedere il valore di co-routine ... quindi posso solo immaginare che pensano implementazioni macro-based (ad esempio di Simon Tatham) come un sostituto adeguato. Non sono, tuttavia, per molte ragioni:. Callee curato Stato, non rientrante, macro-based (che da sola è una ragione sufficiente), etc

Modifica: yield non dipende garbage collection, fili o fibre. Potete leggere l'articolo di Simon a vedere che sto parlando il compilatore fare una semplice trasformazione, come ad esempio:

int fibonacci() {
    int a = 0, b = 1;
    while (true) {
        yield a;
        int c = a + b;
        a = b;
        b = c;
    }
}

Into:

struct GeneratedFibonacci {
    int state;
    int a, b;

    GeneratedFibonacci() : state (0), a (0), b (1) {}

    int operator()() {
        switch (state) {
        case 0:
            state = 1;
            while (true) {
                return a;

        case 1:
                int c = a + b;
                a = b;
                b = c;
            }
        }
    }
}

Raccolta differenziata? No. Le discussioni? No. Le fibre? No. trasformazione semplice? Probabilmente, sì.

È stato utile?

Soluzione

  

Lo hanno fatto per lambda, perché non lo ritengono per supportare la resa, anche tu?

carte . Qualcuno proporlo?

  

... posso solo immaginare che essi considerano le implementazioni macro-based per essere un sostituto adeguato.

Non necessariamente. Sono sicuro che sanno esistono tali soluzioni macro, ma la loro sostituzione non è una motivazione sufficiente, da sola, per ottenere nuove funzioni passati.


Anche se ci sono diverse questioni circa una nuova parola chiave, quelli potrebbe essere superata con nuova sintassi, come è stato fatto per lambda e l'utilizzo di auto come un tipo di funzione di ritorno.

radicalmente nuove caratteristiche hanno bisogno di driver forti (cioè persone) per analizzare completamente e le caratteristiche di spinta attraverso il comitato, in quanto saranno sempre un sacco di persone scettiche di un cambiamento radicale. Quindi, anche assente quello che si potrebbe vedere come un forte motivo di ordine tecnico nei confronti di un costrutto resa, ci possono ancora non sono stati un sostegno sufficiente.

Ma fondamentalmente, libreria standard C ++ ha abbracciato un diverso concetto di iteratori quello che ci si vede con la resa. Confronta iteratori di Python, che richiedono solo due operazioni:

  1. an_iter.next () restituisce l'elemento successivo o solleva StopIteration (next () incorporato incluso in 2.6 invece di utilizzare un metodo)
  2. iter (an_iter) restituisce an_iter (in modo da poter trattare iterables e iteratori identico nelle funzioni)
iterators

C ++ 's vengono utilizzati in coppia (che deve essere dello stesso tipo), sono suddivisi in categorie, sarebbe uno spostamento semantico di transizione in qualcosa più suscettibili di un costrutto resa, e quello spostamento non si adatterebbe bene con concetti (che da allora è stato abbandonato, ma che è venuto relativamente tardi). Ad esempio, vedere la logica per la (giustamente, se deludente) respingendo il mio commento sulla modifica gamma-based per i cicli di una forma che avrebbe fatto scrivere questa diversa forma di iteratore molto più facile.

Per concretamente chiarire cosa intendo quando parlo diverse forme iteratori: il codice di esempio generati esigenze altro tipo di essere il tipo iteratore più macchine associata per ottenere e mantenere quei iteratori. Non che non poteva essere gestita, ma non è così semplice come si potrebbe immaginare in un primo momento. La vera complessità è il "semplice trasformazione" rispetto eccezioni per variabili "locali" (anche durante la costruzione), che controllano durata delle variabili "locali" in ambiti locali all'interno del generatore (più avrebbe bisogno di essere salvato attraverso chiamate), e così via.

Altri suggerimenti

non posso dire perché non aggiungere qualcosa di simile, ma nel caso di lambda, non erano solo aggiunto alla lingua sia.

Hanno iniziato la vita come un'implementazione libreria in Boost, che ha dimostrato che

  • lambda sono ampiamente utili: un sacco di gente li utilizzerà quando sono disponibili, e che
  • un'implementazione libreria in C ++ 03 subisce una serie di carenze.

In base a questo, la commissione ha deciso di adottare qualche tipo di lambda in C ++ 0x, e credo che inizialmente sperimentato con l'aggiunta di un linguaggio più caratteristiche generali per consentire a un migliore implementazione biblioteca di Boost ha.

E alla fine, hanno fatto una caratteristica del linguaggio di base, perché non avevano altra scelta:., Perché non era possibile fare un abbastanza buono biblioteca realizzazione

Le nuove caratteristiche del linguaggio di base non sono semplicemente aggiunto alla lingua perché sembrano una buona idea. Il comitato è molto riluttante ad aggiungerli, e la caratteristica in questione davvero ha bisogno di dimostrare se stesso. Deve dimostrare che la funzione è:

  • possibile l'implementazione nel compilatore,
  • andando a risolvere un bisogno reale, e
  • che un'implementazione libreria non sarebbe abbastanza buono.

Nel caso in cui una parola chiave yield, sappiamo che il primo punto può essere risolto. Come hai mostrato, è abbastanza semplice trasformazione che può essere fatto meccanicamente.

Il secondo punto è difficile. Quanto di un necessità per questo c'è? Come ampiamente utilizzati sono le implementazioni di libreria che esistono? Quante persone hanno chiesto per questo, o presentato proposte per questo?

L'ultimo punto sembra passare troppo. Almeno in C ++ 03, un'implementazione della libreria soffre alcuni difetti, come lei ha sottolineato, che potrebbero giustificare un'implementazione del linguaggio di base. Potrebbe un'attuazione migliore libreria di essere fatto in C ++ 0x però?

Così ho il sospetto che il problema principale è in realtà una mancanza di interesse. C ++ è già una lingua enorme, e nessuno vuole farlo crescere più grande a meno che le caratteristiche che vengono aggiunti sono davvero vale la pena. Ho il sospetto che questo non è abbastanza utile.

L'aggiunta di una parola chiave è sempre difficile, perché invalida codice precedentemente valida. Si tenta di evitare che in una lingua con una base di codice grande come C ++.

L'evoluzione del C ++ è un processo pubblico. Se ti senti yield dovrebbe essere lì, formulare apposita richiesta al comitato standard di C ++.

Si otterrà la risposta, direttamente dalle persone che hanno fatto la decisione.

Così sembra che non ce l'ha fatta in C ++ 11, o C ++ 14, ma potrebbe essere sulla buona strada per C ++ 17. Date un'occhiata alla conferenza C ++ coroutine, un negativo in testa un'astrazione dal CppCon2015 e la carta qui .

Per riassumere, stanno lavorando per estendere le funzioni C ++ avere resa e attendere come caratteristiche di funzioni. Sembra che hanno una prima applicazione in Visual Studio 2015, non è sicuro se clang ha un'implementazione ancora. Inoltre sembra loro possono essere alcuni problemi con l'utilizzo di resa e attendono come le parole chiave.

La presentazione è interessante perché parla su quanto semplificata codice di rete, in cui si è in attesa dei dati di entrare per continuare la sequenza di lavorazione. Sorprendentemente, sembra che l'utilizzo di questi nuovi coroutine risultati nel più veloce / meno codice di quello che si potrebbe fare oggi. E 'una grande presentazione.

La proposta di funzioni recuperabili per C ++ può essere trovato qui .

In generale, è possibile tenere traccia di quello che sta succedendo dalle documenti dei comitati , anche se è meglio per tenere traccia piuttosto che alla ricerca di un problema specifico.

Una cosa da ricordare circa il comitato di C ++ è che si tratta di un comitato di volontari, e non può compiere tutto ciò che vuole. Per esempio, non vi era alcuna hash-tipo di mappa nello standard originale, perché non potevano riuscire a fare in tempo. Potrebbe essere che non c'era nessuno del comitato che ha curato abbastanza su yield e che cosa fa per assicurarsi che il lavoro fosse portato a termine.

Il modo migliore per scoprire sarebbe quello di chiedere ad un membro del comitato attivo.

Bene, per un esempio di così banale come quello, l'unico problema che vedo è che std::type_info::hash_code() non è specificato constexpr. Credo che un conforme attuazione potrebbe ancora fare in modo e sostenere questo. In ogni caso il vero problema è l'ottenimento di identificatori univoci, quindi ci potrebbe essere un'altra soluzione. (Ovviamente ho preso in prestito il vostro "interruttore generale" costrutto, grazie.)

#define YIELD(X) do { \
    constexpr size_t local_state = typeid([](){}).hash_code(); \
    return (X); state = local_state; case local_state: ; } \
while (0)

Utilizzo:

struct GeneratedFibonacci {
    size_t state;
    int a, b;

    GeneratedFibonacci() : state (0), a (0), b (1) {}

    int operator()() {
        switch (state) {
        case 0:
            while (true) {
                YIELD( a );
                int c = a + b;
                a = b;
                b = c;
            }
        }
    }
}

Hmm, avrebbero anche bisogno di garanzia che l'hash non è 0. Nessuna grossa delusione ci sia. E una macro DONE è facile da implementare.


Il vero problema è ciò che accade quando si torna da un ambito con oggetti locali. Non v'è alcuna speranza di salvare fuori uno stack frame in un linguaggio C-based. La soluzione è quella di utilizzare un vero coroutine e C ++ 0x fa direttamente indirizzo che con fili e future.

Consideriamo questo generatore / coroutine:

void ReadWords() {
    ifstream f( "input.txt" );

    while ( f ) {
        string s;
        f >> s;
        yield s;
    }
}

Se un trucco simile è usato per yield, f è distrutta al primo yield, ed è illegale per continuare il ciclo dopo di esso, perché non si può goto o switch passato una definizione di oggetto non-POD.

ci sono stati diversi attuazione coroutine come librerie user-space. Tuttavia, e qui è l'affare, quelle implementazioni si basano su informazioni non standard. Ad esempio, in nessun posto sullo standard C ++ è specificato come stack frame sono conservati. La maggior parte delle implementazioni basta copiare lo stack perché è così che la maggior parte delle implementazioni C ++ lavoro

per quanto riguarda gli standard, c ++ potrebbe avere contribuito supporto coroutine migliorando le specifiche di stack frame.

In realta 'aggiunta' alla lingua non suona una buona idea per me, perché che si bastone con un'implementazione 'abbastanza buono' per la maggior parte dei casi che è interamente compilatore-dipendente. Per i casi in cui utilizzando un questioni coroutine, questo non è accettabile in ogni modo

d'accordo con @Potatoswatter prima.

Per supportare coroutine non è la stessa cosa come supporto per lambda e non che semplice trasformazione come giocato con dispositivo di Duff.

È necessario piena coroutine asimmetrici (stackful ) per funzionare come generatori in Python. L'attuazione di Simon Tatham e Chris' sono entrambi stackless mentre Boost.Coroutine è uno stackfull uno anche se è pesante.

Purtroppo, C ++ 11 ancora non hanno yield per coroutine ancora, forse C ++ 1A;)

PS: Se davvero, come i generatori in stile Python, dare un'occhiata al questo .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top