Signalhandler innerhalb einer Klasse

Hallo - schon wieder! :smile:

Diesmal geht es um Signalhandler. Ich möchte, dass mein Server, wenn er mit CTRL+C beendet wird, noch einiges wieder freigibt.
Dazu hab ich in die Klasse Starter, die den Server an sich startet (ach nee…), eine signalhandler-Funktion eingefügt und in Starter::run die signal. Sieht dann so aus:

STARTER.CPP

#include 
#include 

void Starter::stop(int sig)
{
 cerr 
Leider erhalte ich dabei immer die Compiler-Meldung (VC++ 6):


    
    error C2664: 'signal' : Konvertierung des Parameters 2 von 'void (int)' in 'void (\_\_cdecl \*)(int)' nicht moeglich
     Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem Zieltyp ueberein


Keine Ahnung, was der mit \_\_cdecl\* meint... auch die entsprechende MSDN-Seite hilft nicht weiter.
Interessanterweise funktioniert es sofort, wenn ich die Funktion Stop global mache! (also einfach "Starter::" weglasse).
Aber dann hab ich leider keine Referenzen mehr auf die Klassenobjekte, die ich freigeben will... :/

Kann mir da jemand helfen?

kvida

Zeiger auf Member-Funktionen sind schwierig, und dass der signal-Mechanismus keinen Parameter an die aufgerufene Funktion vorsieht macht das ganze nicht einfacher.

Du solltest in Betracht ziehen, eine globale Resourcenverwaltung aufzubauen, die sich um die Zerstörung in nichttrivialen Fällen kümmert. Im einfachsten Fall wäre das eine referenzierbare Funktion (also global oder static), die Starter::stop() oder den Destruktor aufruft:

// Globals in anonymem Namespace. Sinnfrei aber einfach.
namespace {
 Starter\* g\_starter;
}

void sig\_stopper( int ) {
 delete g\_starter;
 // sonstige Aufräumarbeiten
 exit( 1 );
}

int main( void ) {
 // ...
 g\_starter = new Starter;

 //...
 signal( SIGINT, sig\_stopper );

 //...
 delete g\_starter;
 return 0;
}

Ansonsten würde mir jetzt nur eine andere Methode einfallen, die gerne angewendet wird: der Handler setzt ein Flag, das regelmäßig geprüft wird und das Programm beendet sich dann ordnungsgemäß:

namespace {
 bool g\_quit\_flag( false );
}

void sig\_stopper( int ) {
 g\_quit\_flag = true;
}

int main( void ) {
 int ret(0); // kann gesetzt werden, wenn sonstwas schiefgeht

 signal( SIGINT, sig\_stopper );

 // "Hauptschleife"
 while( !g\_quit\_flag && !ret ) {
 // Einmal arbeiten bitte...
 }

 // normales Beenden
 return ret;
}

Das funktioniert natürlich nur, wenn die Hauptschleife nicht unendlich blockieren kann.

Klappt super, vielen Dank!

Zeiger auf Member-Funktionen sind schwierig, […]

Ich verstehe nicht warum, aber ok…

Deine erste Methode hat super geklappt! Ab sofort hab ich einen „sauberen“ Server! :smile:

Vielen Dank!

kvida

Zeiger auf Member-Funktionen sind schwierig, […]

Ich verstehe nicht warum, aber ok…

Weil die Funktionen nur einmal als Funktionssymbol existiert. Die Zauberei bei der objektorientierten Programmierung besteht darin, dass der Compiler implizit allen Methoden als impliziten Parameter das „this“ des Objekts mitgibt, das eine Struktur mit allen Attributen umfasst. Ein Zeiger auf eine Member-Funktion müsste also prinzipiell aus zwei Zeigern, nämlich auf die Funktion selber und auch die Struktur mit den Attributen, bestehen.

Eine einfache C+±Klasse ohne Vererbung kann trivial nach C konvertiert werden:

// C++
class A {
 int a;
 int b;

 public:
 A( int \_a ) : a(\_a), b(-1) {}

 int get\_a( void ) { return a; }
 int get\_b( void ) { return b; }
};

/\* C \*/
struct A {
 int a, b;
};

void A\_construct( struct A \*this, int \_a ) {
 this-\>a = \_a;
 this-\>b = -1;
}

int A\_get\_a( struct A \*this ) { return this-\>a; }
int A\_get\_b( struct A \*this ) { return this-\>b; }

Oh, es geht weiter… -_-
Hallo,

warum verweigert der Signalhandler seinen Dienst, wenn ich den Starter als Singleton aufbaue?
Hier mal der Code:

/\* MAIN.CPP \*/
int main()
{
 Starter::instance()-\>run();
 return 0;
}


/\* STARTER.H \*/
class Starter
{
private:
 static Starter\* singleton; // die einzige Referenz
 Starter();
 Starter(const Starter& copy);
 ~Starter();
public:
 static Starter\* instance();
 void run(); // Server starten
 void stop(); // Server stoppen
};


/\* STARTER.CPP \*/
void stop(int sig)
{
 cerr stop();
}

void Starter::run()
{
 signal(SIGINT, stop); // 

Die Fehlermeldung ist immer noch die gleiche:

"error C2664: 'signal' : Konvertierung des Parameters 2 von 'void (int)' in 'void (\_\_cdecl \*)(int)' nicht moeglich
 Keine Funktion mit diesem Namen im Gueltigkeitsbereich stimmt mit dem Zieltyp ueberein"

Und der Gedankengang war folgender:
Meine Signalhandler stop()-Funktion braucht eine Referenz auf Starter, um Starter::stop() aufzurufen. Also muss das Starterobjekt global sein. Wenn ich es "erst" in main() erzeuge, ist das schon nicht mehr global. Wenn ich es aber als Singleton implementiere, gibt es beim Anlegen eines Starterobjekts programmweit immer die gleiche Referenz!
Also kann ich in stop() durch "Anlegen" von Starter die Referenz bekommen!...

Leider sieht der Compiler das anders. :frowning:

Irgendwelche Ideen?

kvida