Gestione delle eccezioni con puntatori a funzione

di il
5 risposte

Gestione delle eccezioni con puntatori a funzione

Gentili utenti,
sono Matteo, un nuovo iscritto, e vorrei porvi un mio dubbio riguardo il funzionamento della parola di vocabolario catch.
Nel seguente codice la mia intenzione è quella di inizializzare il puntatore ptr all'interno del catch passandoglielo per riferimento e non per copia, così da potermelo ritrovare nel main, fuori dal catch, che punta la funzione funz.

#include <iostream>
int funz() {std::cout << "funz\n"; return 1;}
int main()
{
    int (*ptr)() = nullptr;
    try{throw ptr;}
    catch(int (*&po)()) {
    po = funz;
    po();
    }
    ptr();
}
Non riesco però a capire perché l'espressione ptr() mi genera una segmentation fault.
Tutta via se il puntatore lo dichiaro all'interno del catch come un riferimento e lo inizializzo il problema non si presenta. Per intenderci, nella seguente variante

#include <iostream>
int funz() {std::cout << "funz\n"; return 1;}
int main()
{
    int (*ptr)() = nullptr;
    try{throw ptr;}
    catch(int (*)()) {
    int (*&po)() = ptr;
    po = funz;
    po();}
    ptr();
}
il segmentation fault non si verifica e l'output del programma è

funz
funz
come da attesa.
Ringrazio chiunque voglia aiutarmi a capire la differenza funzionale tra le due versioni del codice e perché la prima non funziona.

5 Risposte

  • Re: Gestione delle eccezioni con puntatori a funzione

    Ciao
    non ho capito bene quello che vuoi capire!
    comunque se ti riferisci al fatto del segment fault si verifica perchè non è inizializzato nel primo caso mentre nel secodo lo è.
  • Re: Gestione delle eccezioni con puntatori a funzione

    smalldragon ha scritto:


    Ciao
    non ho capito bene quello che vuoi capire!
    comunque se ti riferisci al fatto del segment fault si verifica perchè non è inizializzato nel primo caso mentre nel secodo lo è.
    Ciao
    Innanzi tutto grazie per la risposta. Quello che non capisco è proprio perché nel primo caso ptr non sia inizializzato. Poiché nel primo caso nelle parentesi tonde del catch ho inserito & mi aspetto che quando faccio
    
    po = funz
    
    venga inizializzato ptr ma non riesco a capire perché ciò non accada.
  • Re: Gestione delle eccezioni con puntatori a funzione

    La spiegazione la trovi qui:
    http://en.cppreference.com/w/cpp/language/try_catc
    sotto Explanation.
    If the type of the formal parameter is array type or function type, it is treated as the corresponding pointer type (similar to a function declaration)
    Vale a dire che l'& del catch viene scartato, e quel int(*&po)() decade in int(*po)(). Di conseguenza po non è un reference ma un semplice puntatore interno al blocco catch e pertanto non porta eventuali assegnamenti all'esterno.
    Nel secondo codice invece po è a tutti gli effetti un reference e si comporta come tale.
    Non capisco però perché complicarsi la vita in quel modo.
  • Re: Gestione delle eccezioni con puntatori a funzione

    La spiegazione di shodan è perfetta nulla da aggiungere
  • Re: Gestione delle eccezioni con puntatori a funzione

    Salve,
    grazie mille per la risposta.
    Tuttavia credo che la frase che lei ha citato si riferisse al fatto che se un catch ha come argomento un array di un certo tipo può ricevere eccezioni che sono puntatori a quel tipo, e che se un catch ha come argomento una funzione può ricevere eccezioni che sono puntatori a funzione (esattamente come accade nella dichiarazione di una funzione).
    Come ad esempio accade in
    
    #include <iostream>
    
    int main () {
        void (*p)(); 
        try{throw p;}
        catch(void ()){std :: cout << "ciao, sono il catch(void ())!" << '\n';}
        int *l;
        try{throw l;}
        catch(int[ ]) {std :: cout << "ciao, sono il catch(int [ ])!" << '\n';}
    
    }
    

    Riguardandomi per bene l'argomento credo di aver trovato la risposta, che riporto qualora servisse a qualcun altro.
    http://en.cppreference.com/w/cpp/language/thro
    First, copy-initializes the exception object from expression (this may call the move constructor for rvalue expression, and the copy/move may be subject to copy elision), then transfers control to the exception handler with the matching type...
    Il fatto di ricevere un'eccezione come riferimento non evita il fatto che venga fatta una copia nel momento del throw; il passaggio per riferimento nel catch è efficace nel momento in cui in un catch viene inserito
    throw;
    a questo punto l'eccezione viene rilanciata nel livello di stack superiore e qualora trovasse un catch adatto che riceva l'eccezione per riferimento riceverebbe l'eccezione senza che venga fatta alcuna copia.
    Ad esempio nel seguente codice
    
    #include <iostream>
    int (*ptr)() = nullptr;
    int funz() {std::cout << "funz\n"; return 1;}
    void gunz(){try{throw ptr;}
    catch(int (*&l)()){l = funz; throw;}
    }
    int main()
    {
        
        try{gunz();}
        catch(int (*&po)()) {
        po();
        }
        
    }
    
    l'inizializzazione che l subisce nel catch di gunz arriva al catch di main visto che l'eccezione è stata rilanciata e ricevuta per riferimento. Infatti l'espressione
    po();
    non genera segmentation fault. In tutto ciò ptr rimane non inizializzato.
Devi accedere o registrarti per scrivere nel forum
5 risposte