Puoi passare un'etichetta come argomento e avere la funzione di tornare ad essa?

1

Quindi mi piacerebbe poter chiamare una funzione come questa:

void func(1, 2, 3, (void*)label) // can return normal or to the labels
//some code
label:
//different code

È possibile, ed è una cattiva pratica?

    
posta JavaProphet 27.06.2016 - 20:24
fonte

3 risposte

8

In C99 o C11 standard non puoi.

Ma GCC ha un'estensione linguistica, etichetta come valori , che potrebbe essere di aiuto (e Clang / LLVM lo accetta anche) .

Anche con il gotos calcolato fornito dall'estensione, saltare in un'altra routine è (quasi) un comportamento indefinito. Puoi saltare indirettamente a un'etichetta all'interno della stessa stessa . Puoi restituire un'etichetta in cui saltare; ovvero, goto *fun(&&label1, &&label2, x, y); con fun(void*l1, void*l2, int x, int y) che restituisce uno ( l1 o l2 ) dei suoi primi due argomenti. Puoi avere codice threadato .

Si noti che su molti processori, un salto indiretto (o chiamata) è più lento di uno diretto (perché danneggia il precedente di branch ). Quindi non penso che restituire un'etichetta per una percentuale indiretta di% co_de sia una buona pratica (quindi la risposta di John Bode suggerire un goto è meglio), ma potresti avere strani casi d'angolo.

Sappi anche che il recente GCC (cioè GCC 6 nel giugno 2016) può a volte ottimizza per fare una richiamo .

Leggi anche su stile di passaggio continuo (e anche su continuazioni & continuazioni delimitate , call / cc in Scheme , < a href="https://en.wikipedia.org/wiki/Callback_(computer_programming)"> callback , chiusure e forse loop di eventi ; Prolog e le sue backtracking abilità; coroutine ; iteratori ; discussioni ; eccezioni ; semantica operativa & semantica denotazionale ). Potrebbe ispirarti; guarda all'interno dell'implementazione di Schema di pollo e leggi Cheney sul MTA e il SICP libro. Se stai generando un codice C (ad esempio da un linguaggio simile a Lisp) o vuoi capire come funziona Lisp o Scheme, leggi anche Lisp In Small Pieces book.

Per la gestione delle eccezioni di basso livello in C, longjmp (con switch ) è il modo preferito (si veda anche lo specifico obsoleto, Unix, setcontext (3) ...; controlla backtrace (3) . ..). Ma fa attenzione. Vedi questo & che .

    
risposta data 27.06.2016 - 20:54
fonte
4

Puoi (vedi le etichette come Valori estensione gcc) . È piuttosto dubbio che dovresti.

Dijkstra ci ha insegnato che goto era dannoso . Stai proponendo non solo l'uso di goto e una tabella di salto, ma anche una funzione che ritorna all'etichetta passata.

Comprendi che una funzione è essenzialmente un goto (salto) con un po 'di spinta sullo stack (il frame dello stack). Quella cornice è pop-up su un ritorno per mettere lo stack in uno stato previsto. Se intendi sovvertire il ritorno della funzione nella sua posizione di chiamata, per andare invece all'etichetta passata, allora dovresti sapere come ripulire lo stack e avere il proprio modo di tornare al blocco principale di codice o alcuni idea di come il programma dovrebbe continuare da qui.

Trucchi come questo sono come funzionano le funzioni in primo luogo. Se lo desideri, puoi progettare il tuo protocollo di funzione in questo modo. La domanda è perché? Le funzioni lo fanno già. Puoi risolvere molti problemi che potrebbero richiedere questo con le funzioni, e se hai bisogno di essere fantasioso, puntatori di funzioni .

    
risposta data 27.06.2016 - 20:57
fonte
3

Non è possibile, e se lo fosse, sarebbe probabilmente considerato una cattiva pratica 1 . Gotos all'interno di una funzione rende il codice impossibile da tracciare con l'ispezione; le funzioni gotos across potrebbero peggiorare la situazione molto .

Ecco un esempio:

        i = 4;
label:  printf( "%d\n", i );

Quale valore viene effettivamente stampato? È 4 ? È mai 4 ? Non posso sapere fino a quando non inseguo ogni istanza di goto label in quella funzione. Supponiamo che io scopra qualcosa del genere:

        i = 2;
        goto label;
        ...
        i = 4;
label:  printf( "%d\n", i );

In questo scenario, i è mai 4 ; questo incarico viene saltato incondizionatamente. Ora, immagina che questo tipo di cose possa accadere attraverso e il lavoro diventa ancora più brutto.

Puoi gestire la gestione delle eccezioni con l'utilizzo di setjmp e longjmp , ma non è esattamente quello che stai cercando. Onestamente, avrei func restituire un codice di errore e un ramo basato su quello:

switch ( func( 1, 2, 3 ) )
{
  case NORMAL:  
    // normal processing
    break;

  case THIS_ERROR: 
    // error processing
    break;

  case THAT_ERROR: 
    // error processing
    break;

  ...
}

o

if ( func( 1, 2, 3 ) )
{
  // normal processing
}
else
{
  // error processing
}

  1. Questo mi sta dando dei flashback su un pezzo di codice particolarmente sgradevole che ho incontrato all'inizio della mia carriera, dove abbiamo letteralmente speso settimane cercando di scoprire il flusso del controllo.

risposta data 27.06.2016 - 20:54
fonte

Leggi altre domande sui tag