2012-10-12 1 views
5

Ho un oggetto che funziona in un ciclo infinito. Lo main() crea un'istanza dell'oggetto e chiama il metodo run(). Dal momento che non voglio usare i thread, ho bisogno di una soluzione per far sì che il mio oggetto smetta di funzionare. Sotto vedi quello che ho scoperto.Segnali di cattura: utilizzare una funzione membro come gestore di segnale

struct Foo 
{ 
    void run() 
    { 
     running = 1; 
     while (running) 
      do_something_useful(); 

     std::cout << "Execution stopped." << std::endl; 
    } 

    bool running; 

    void catch_signal(int signal) 
    { 
     std::cout << "Caught signal " << signal << std::endl; 
     if(signal == SIGTERM) 
      running = false; 
    } 

}; 

Come si vede, ho bisogno di inviare un segnale in modo asincrono. Pertanto, io uso un gestore di segnale e sigaction. Sotto il main posso immaginare di usare.

int main(int argc, char** argv) 
{ 
    Foo foo; 
    struct sigaction sigIntHandler; 

    boost::function< void (int) > f; 
    f = std::bind1st(
     std::mem_fun(&Foo::catch_signal), &foo); 
    f(5); // this call works 

    sigIntHandler.sa_handler = f;   // compiler complains, "cannot assign ..." 
    sigemptyset(&sigIntHandler.sa_mask); 
    sigIntHandler.sa_flags = 0; 
    sigaction(SIGTERM, &sigIntHandler, NULL); 
    s.run(); 

} 

Cosa mi aspetto ora: il programma viene eseguito fino a quando io mando SIGTERM che viene catturata e farà sì che il mio oggetto di fermare l'iterazione e tornare alla principale.

Ho due domande ora:

(a) il codice che vedi una linea contrassegnata con "compilatore si lamenta", il messaggio è come

boost::function<void(int)> cannot be converted to __sighandler_t {aka void (*)(int)} 

Di cosa ho bisogno di cambiare per rendere questo lavoro? Penso che f sia come void f(int), come le funzioni che il gestore di segnali riceve in alcuni esempi.

(b) Per quelli di voi che si chiedono "cosa sta facendo quel ragazzo?": Avete qualche consiglio su come risolvere meglio questo tipo di cose?

+0

Solo per curiosità, perché non desideri utilizzare i thread?Anche se non ho usato boost, suppongo che si aspetti una funzione di callback da fornire. – M4rc

+0

Come avviare '' Foo :: run() '' in una discussione, catturando il segnale in '' main'' e lasciare che la chiamata principale sth. come '' thread.terminate() ''? Sì, sarebbe una possibilità, ma ho pensato che sarebbe stato troppo per questo. –

+0

Questo è un modo per sicuro. L'altro che (nel mio miasma di pensiero sedato) è che si può avere una struttura con qualsiasi informazione in essa necessaria, registrarla come thread, in modo da eseguire il ciclo con le normali funzioni principali al suo interno e il gestore di segnale in esecuzione sul proprio thread, quindi prendi il valore byref per vedere se è successo un evento, e in tal caso quale evento e la risposta appropriata. – M4rc

risposta

7
  • Cosa devo cambiare per farlo funzionare? Penso che f sia come void f (int), come le funzioni che il gestore del segnale ottiene in alcuni esempi.

Il compilatore si lamenta del tipo, quindi, è necessario passare un puntatore a funzione, non è un oggetto di tipo boost::function<void(int)>. Creazione di una variabile globale di questo tipo, e l'aggiunta di una funzione che chiama questo oggetto dovrebbe funzionare:

boost::function<void(int)> myCb; 
void CallCb(int value) 
{ 
    myCb(value); 
} 

int main(int argc, char** argv) 
{ 
    Foo foo; 
    struct sigaction sigIntHandler; 

    myCb = std::bind1st(
     std::mem_fun(&Foo::catch_signal), &foo); 
    f(5); // this call works 

    sigIntHandler.sa_handler = CallCb; 
    sigemptyset(&sigIntHandler.sa_mask); 
    sigIntHandler.sa_flags = 0; 
    sigaction(SIGTERM, &sigIntHandler, NULL); 
    s.run(); 

} 
  • Hai qualche consiglio come risolvere questo genere di cose meglio?

Non proprio. L'idea è ok. Vorrei solo C++ 11 lambda invece

+1

Grazie soprattutto per aver sottolineato che '' f'' non è una funzione ma un oggetto a cui non ho pensato. –

+0

Puoi dare un esempio di come lo faresti con un lambda C++ 11? – Azmisov

2

C'è pochissimo che puoi fare in modo portabile in un gestore di segnale. In sostanza, è possibile memorizzare un valore in un oggetto il cui tipo è sig_atomic_t. L'inserimento in cout, ad esempio, non deve funzionare. Se hai C++ 11 puoi fare un po 'di più, usando i tipi atomici o le recinzioni esplicite, ma a qualsiasi altra chiamata nella libreria standard non è richiesto di fare qualcosa di sensato.

Quindi, con tutto ciò che detto, ciò che puoi fare è scrivere una funzione (una funzione libera o una funzione membro statica (ma qui c'è un problema sottile con il collegamento C++: formalmente una funzione membro statica non funzionerà , ma in pratica lo fa sempre)) che chiama la funzione membro, che a sua volta imposta running su false (presumendo che tu abbia cambiato il tipo di running in sig_atomic_t).