[gelöst] Templateklasse mit static membervariablen; Konstruktor wird nicht aufgerufen, keine Instanz erstellt...?



  • Hallo zusammen,

    ich habe ein ..."kleines" Problem.

    Ich habe zwei Templateklassen, MutexFactory und MutexSharedMem.
    Die MutexFactory erstellt eine(!) Instanz von MutexSharedMem, wenn noch nicht vorhanden, sonst gibt sie eine Referenz auf das schon erstellte MutexSharedMem zurück.

    Die MutexFactory hat statische Membervariablen:
    - refCount zum Zählen der derzeit vorhandenen Referenzen auf das MutexSharedMem
    - Einen Zeiger auf das MutexSharedMem

    Ich werde auch noch den Code hier reinposten, keine Sorge, den habe ich aber gerade leider nicht zur Hand.

    Folgendes: Wenn ich diese Konstrukte in einem eigenen dafür erstellten Testprojekt benutze, klappt alles wunderbar.
    MutexFactory-Konstruktor wird aufgerufen, ich kann eine Referenz auf ein MutexSharedMem zurückgeben lassen und danach kann ich die MutexFactory wieder zerstören.

    Wenn ich die beiden Klassen allerdings in einem anderen, "richtigen" Projekt verwenden will, zeigt die Debug-Ausgabe an, daß der Konstruktor nicht mehr aufgerufen wird, und somit auch keine Instanz von MutexSharedMem erstellt werden kann. Spätestens, wenn man dann nachher mit dem MutexSharedMem arbeiten will, gibt das Programm auf.

    Ich kann den Fehler einfach nicht lokalisieren, also hier erstmal die Frage:

    Wann wird ein Konstruktor einer Templateklasse mit statischen Membern nicht aufgerufen, wenn man mit new oder auch als normale Referenz ein Objekt erstellt?



  • Der ctor wird aufgerufen wenn das Objekt konstruiert wird. Ob die Klasse statische Member hat spielt dabei keine Rolle. Du musst also schon den Code posten, ich zumindest weiss sonst wirklich nicht was du da genau versuchst zu machen, und was du falsche gemacht haben könntest.



  • Wie gesagt, ich hab den Code nicht hier, heute abend poste ich ihn sofort.

    Vielleicht noch ein Punkt: Der Destruktor der MutexFactory wird aufgerufen, der Konstruktor aber nicht.

    Der Konstruktor des MutexSharedMem ist privat, die Klasse hat aber die MutexFactory als friend.



  • Okay, nicht wirklich richtig gute, aber immerhin kann ich jetzt schon den Code posten.

    MutexFactory.h:

    /*
     * Klasse: MutexFactory
     *
     * Author: ich
     *
     * Zweck:
     * - Baut MutexSharedMem-Objekte eines Typs (mit Templates)
     * - sorgt dafuer, daß nur ein Objekt von einem MutexSharedMem-Typ vorhanden ist
     * - zaehlt, wieviel Referenzen auf ein MutexSharedMem existieren
     * - ist für das zerstoeren der MutexSharedMem-Objekte verantwortlich (mit destroy)
     *
     * Daten:
     * refCount - Anzahl der Referenzen eines Typs
     * factoryMutex - von der Fabrik benoetigter Mutex fuer gleichzeitiges Erstellen von Referenzen
     * sharedMem - Zeiger auf ein MutexSharedMem eines Typs T
     */
    
    #ifndef MUTEXFACTORY_H_
    #define MUTEXFACTORY_H_
    
    #include <pthread.h>
    #include "MutexSharedMem.h"
    
    #undef MODULENAME
    #define MODULENAME "MutexFactory::"
    
    using namespace std;
    
    template <class T>
    class MutexFactory {
    	private:
    		static int refCount;
    		static pthread_mutex_t factoryMutex;
    	protected:
    		static MutexSharedMem <T> *sharedMem;
    	public:
    		// erstellt eine Mutex-Fabrik mit 0 Referenzen und keinem existierenden sharedMem
    		MutexFactory ();
    		// zerstoert die Mutex-Fabrik; da aber andere Fabriken existieren können,
    		// wird das sharedMem erst zerstoert, wenn keine Referenzen mehr darauf sind (mit destroy)
    		virtual ~MutexFactory ();
    		// erstellt ein MutexSharedMem, falls noch nicht existent; gibt Zeiger auf MutexSharedMem
    		MutexSharedMem <T>* create ();
    		// zerstoert eine Referenz auf ein MutexSharedMem
    		// Zaehler wird runtergesetzt
    		// zerstoert das MutexSharedMem, wenn die letzte Referenz vernichtet ist
    		void destroy (MutexSharedMem <T>*);
    };
    template <class T>
    int MutexFactory <T>::refCount = 0;
    template <class T>
    MutexSharedMem <T> *MutexFactory <T>::sharedMem = 0;
    template <class T>
    pthread_mutex_t MutexFactory <T>::factoryMutex = PTHREAD_MUTEX_INITIALIZER;
    
    template <class T>
    MutexFactory <T>::MutexFactory () {
    	cout << MODULENAME << "Constructor" << endl;
    }
    
    template <class T>
    MutexFactory <T>::~MutexFactory () {
    	sharedMem = 0;
    	cout << MODULENAME << "Destructor" << endl;
    }
    ;
    
    template <class T>
    MutexSharedMem <T>* MutexFactory <T>::create () {
    	cout << MODULENAME << "read " << refCount << endl;
    	if (pthread_mutex_lock (&factoryMutex) == 0) {
    		if (refCount == 0) {
    			sharedMem = new MutexSharedMem <T> ();
    		}
    	}
    	refCount += 1;
    	pthread_mutex_unlock (&factoryMutex);
    	return sharedMem;
    }
    
    template <class T>
    void MutexFactory <T>::destroy (MutexSharedMem <T>* oldSharedMem) {
    	if (oldSharedMem != NULL) {
    		if (pthread_mutex_lock (&factoryMutex) == 0) {
    			if (refCount == 1) {
    				delete oldSharedMem;
    			}
    		}
    		oldSharedMem = 0;
    		refCount -= 1;
    		pthread_mutex_unlock (&factoryMutex);
    	} else {
    		cout << MODULENAME << "destroy: pointer to sharedMem is NULL!" << endl;
    	}
    }
    
    #endif /* MUTEXFACTORY_H_ */
    

    MutexSharedMem.h:

    /*
     * Klasse: MutexSharedMem
     *
     * Autor: ich
     *
     * Zweck: Ein SharedMem, welches den Zugriff mit Mutexen verwaltet
     *
     * Daten:
     *   signalMutex - Mutex fuer die Signalisierung neuer Daten zum Austausch
     *   memMutex - Mutex fuer den Speicherzugriff
     *   data - die Daten, die zwischen Prozessen/Modulen/Threads ausgetauscht werden sollen
     */
    
    #ifndef MUTEXSHAREDMEM_H_
    #define MUTEXSHAREDMEM_H_
    
    #include <pthread.h>
    #include <iostream>
    #include <stdlib.h>
    
    #define MODULENAME "MutexSharedMem::"
    
    using namespace std;
    
    template <class T> class MutexFactory;
    
    template <class T>
    class MutexSharedMem {
    	friend class MutexFactory<T>;
    	private:
    		pthread_mutex_t signalMutex;
    		pthread_mutex_t memMutex;
    		T *data;
    		// initialisiert das MutexSharedMem mit leeren Werten
    		void init();
    		void init(T&);
    		void destroy();
    		// erstellt ein MutexSharedMem ohne Daten
    		// ruft den Default-Konstruktor fuer die Daten auf
    		// initialisiert Mutexe
    		MutexSharedMem ();
    		MutexSharedMem(MutexSharedMem&);
    		MutexSharedMem& operator=(const MutexSharedMem&);
    	public:
    		// Default-Destruktor; loescht die Daten im SharedMem
    		virtual ~MutexSharedMem ();
    		// liest das SharedMem aus und gibt eine Kopie davon zurueck
    		// funktioniert nur einmal, dann schreiben neuer Daten mit write noetig; wartet solange
    		T *read ();
    		// schreibt Daten in das SharedMem
    		// funktioniert nur einmal, dann lesen der Daten mit read noetig
    		void write (T*);
    };
    
    template <class T>
    void MutexSharedMem <T>::init() {
    	data = 0;
    	return;
    }
    
    template <class T>
    void MutexSharedMem <T>::init(T &newData) {
    	init();
    	data = new T(newData);
    }
    
    template <class T>
    void MutexSharedMem <T>::destroy() {
    	if (data != 0) {
    		delete data;
    		data = 0;
    	}
    }
    template <class T>
    MutexSharedMem <T>::MutexSharedMem(MutexSharedMem<T> &original) {
    	init(*(original.data));
    }
    
    template <class T>
    MutexSharedMem<T> &MutexSharedMem <T>::operator= (const MutexSharedMem<T> &original) {
    	destroy();
    	init(*(original.data));
    	return *this;
    }
    
    template <class T>
    MutexSharedMem <T>::MutexSharedMem () {
    	this->init();
    	if (data == 0) {
    		pthread_mutex_t newSignalMutex = PTHREAD_MUTEX_INITIALIZER;
    		pthread_mutex_t newMemMutex = PTHREAD_MUTEX_INITIALIZER;
    		signalMutex = newSignalMutex;
    		memMutex = newMemMutex;
    		pthread_mutex_lock (&signalMutex);
    		data = new T ();
    	} else {
    		cout << MODULENAME << "Critical Error! data already points to something!" << endl;
    	}
    }
    
    template <class T>
    MutexSharedMem <T>::~MutexSharedMem () {
    	destroy();
    }
    
    template <class T>
    T *MutexSharedMem <T>::read () {
    	T *memContent = new T ();
    	if (pthread_mutex_lock (&(this->signalMutex)) == 0) {
    		if (pthread_mutex_lock (&(this->memMutex)) == 0) {
    			*memContent = *data;
    			pthread_mutex_unlock (&(this->memMutex));
    		}
    	}
    	return memContent;
    }
    
    template <class T>
    void MutexSharedMem <T>::write (T *newData) {
    	cout << MODULENAME << "read" << endl;
    	if (newData != NULL) {
    		if (pthread_mutex_lock (&(this->memMutex)) == 0) {
    			*data = *newData;
    			pthread_mutex_unlock (&(this->memMutex));
    			pthread_mutex_unlock (&(this->signalMutex));
    		}
    	} else {
    		cout << MODULENAME << "write: new data pointer is NULL!" << endl;
    	}
    }
    #endif /* MUTEXSHAREDMEM_H_ */
    

    Benutzen tu ich das ganze mit folgendem Codestück...

    "Erschaffen":

    MutexFactory<int> *factory = new MutexFactory<int>;
    MutexSharedMem<int> *share = factory->create();
    delete factory;
    factory = 0;
    

    Das funktioniert halt in dem Testprojekt gut, alles läuft so ab wie erwartet. In diesem Testprojekt sind nur die beiden Header oben, eine main.cpp und zwei Dateien (.h und .cpp), die eine Klasse beschreiben, die statt <int> genommen zum Austausch genommen wird.

    Bei Bedarf kann ich den Code dafür auch noch posten.

    Allerdings wird dieser Code auch in dem "normalen" Projekt verwendet, und dort funktioniert es nicht.

    Was aufgetreten ist:
    Wenn ich in der main des "normalen" Projektes oben genannte Prozedur zum erstellen von MutexFactory und MutexSharedMem benutze, und irgendwo in anderen Dateien im Projekt ähnliches getan wird, wird kein Konstruktor von MutexFactory aufgerufen.

    Wenn allerdings alle Vorkommen von "Erschaffen" aus den anderen Dateien auskommentiert sind, wird der Konstruktor der MutexFactory benutzt.

    Der Destruktor wird immer am Ende aufgerufen.



  • Okay, das mit dem nicht aufgerufenen Konstruktor hat sich erledigt, ich hab den Wald vor lauter Bäumen nicht gesehen. Eclipse hat ohne mich zu fragen die Header-Dateien, die nicht im /include-Verzeichnis waren, dort hinein kopiert.

    Deswegen existierten die doppelt im System. Die Variante, die für das Kompilieren benutzt wurde, war eine alte (!), wo noch nicht der Kommentar im Konstruktor stand.

    Dieses Problem ist gelöst, jetzt muss ich nur schauen, warum das Programm mittendrin terminiert ^^


Log in to reply