gemeinsame Abstraktion für ein scoped-Lock gesucht, das dann entweder von boost oder Qt implementiert wird



  • Servus,

    etwas kryptischer Thread-Titel, aber das Problem ist glaube ich schnell erklärt:
    Ich habe eine statische Library, von denen eine Klasse einen mutex-lock benötigt. Derzeit ist das über das boost::mutex::scoped_lock implementiert.

    Die library wird in eine Applikation gelinkt, in die auch boost_thread reingelinkt wird.

    Jetzt möchte ich die library in einer weiteren Applikation verwenden. Diese Applikation basiert auf Qt und ich will nur sehr ungern sowohl Qt als auch boost in selbige linken, zumal es ja auch mutex-implementierungen von Qt gibt.

    Was ich jetzt suche ist ein Weg, eine gemeinsame Abstraktion eines "scoped locks" zu haben, der wenn die library zusammen mit boost gelinkt wird, die entsprechende Mutex von boost verwendet, wenn die library hingegen mit Qt gelinkt wird, das entsprechende Qt equivalent.

    Wie sollte ich da vorgehen?

    Gruß,
    Phil



  • Eigene Klasse schreiben die nur als Interface dient.
    Die Klasse leitest du dann ab und implementierst jeweils den boost und qt mutex dahinter.
    Dann kannst du einfach über Präprozessor defines festlegen welche genommen werden soll.

    class MyScopedLock
    {
    // blub
    };
    
    class MyBoostScopedLock : MyScopedLock
    {
    // blub
    };
    
    class MyQtScopedLock : MyScopedLock
    {
    // blub
    };
    
    // ...
    
    #ifdef USE_BOOST
    #include "MyBoostScopedLock.h"
    #else
    #include "MyQtScopedLock.h"
    #endif
    
    // ...
    
    #ifdef USE_BOOST
    MyBoostScopedLock lock;
    #else
    MyQtScopedLock lock;
    #endif
    

    Hmm... das Ableiten von einer gemeinsamen Klasse könntest du dir auf die Weise auch sparen, macht es aber meiner Meinung nach übersichtlicher bzw. klarer um was es geht.



  • Interface definieren, zwei verschiedene Implementationen basierend auf Qt bzw. boost anbieten und jeweils entsprechend linken. Den Vorschlag von blub halte ich fuer bloed, da der gesammte Quelltext geaendert werden muss, falls man Qt benutzen will, wenn man vorher boost hatte oder aber mit defines sich den Quelltext beschmiert. (jaja, es gibt typedef ...)



  • @knivil
    Ich sehe da jetzt nicht wirklich einen Unterschied:
    Bei meinem Beispiel wird auch einfach nur das passende gelinkt.
    Außerdem kann man ja das Interface der MyScopedLock Klasse wenn man mag genau so gestalten wie das des boost::scoped_lock, oder eben entsprechend dem Qt Scoped Lock, je nachdem was zuerst verwendet wurde.
    Ich gehe dabei jetzt davon aus das nur eine der beiden Implementationen bisher geschrieben wurde.



  • knivil schrieb:

    Interface definieren, zwei verschiedene Implementationen basierend auf Qt bzw. boost anbieten und jeweils entsprechend linken. Den Vorschlag von blub halte ich fuer bloed, da der gesammte Quelltext geaendert werden muss, falls man Qt benutzen will, wenn man vorher boost hatte oder aber mit defines sich den Quelltext beschmiert. (jaja, es gibt typedef ...)

    Aber wenn man ein "Interface implementiert" muss man ja immer über einen pointer oder eine Referenz auf das Interface zugreifen, damit man in der korrekten abgeleiteten Klasse landet. Der Trick von boost::mutex::scoped_lock ist ja aber gerade, dass man ein Objekt auf dem Stack anlegt, und keinen Pointer auf ein Heap-Objekt, damit es gerade beim verlassen des scopes aufgeräumt wird.
    Außerdem kann ich nicht mit defines im Code der library arbeiten, denn die will ich nur einmal kompilieren. Ich will nicht zwei versionen der library kompileren und zum linken anbieten, sondern EINE einzige library, deren mutex-implementierung dadurch entschieden wird was man noch ZUSÄTZLICH hinzu linkt.

    Ich hoffe, ich habe mich verständlich ausgedrückt.



  • Und seit wann genau liegen Referenzen nicht auf dem Stack?

    class foo
    {
    public: virtual void bla(){};
    		virtual ~foo() = 0 {};
    };
    
    class bar1 : public foo
    {
    public: void bla(){ cout << "bla1\n"; }
    		~bar1(){ cout << "dtor bar1\n"; }
    };
    
    class bar2 : public foo
    {
    public: void bla(){ cout << "bla2\n"; }
    		~bar2(){ cout << "dtor bar2\n"; }
    };
    
    void blub()
    {
    	foo &f1 = bar1();
    
    	f1.bla();
    
    	foo &f2 = bar2();
    
    	f2.bla();
    }
    
    int main()
    {
    	blub();
    
    	_getch();
    
    	return 0;
    }
    

    Ausgabe:

    bla1
    bla2
    dtor bar2
    dtor bar1
    


  • "Interface implementiert"

    Interface ist einfach nur eine gemeinsame Schnittstelle, nix mit virtual oder Vererbung.

    will ich .. Ich will

    Das, was du willst, geht nicht. Linken ist nach dem Kompilieren.



  • Also wie ich es "zaubern" soll, das in

    foo& f1 = bar1();
    

    bar1() in einem teil des Projektes magisch durch "bar2()" ersetzt wird, entzieht sich leider meiner Kenntnis.

    Ich weiß sehrwohl dass die Linkzeit nach der Kompilierzeit ist und nur gelinkt wird, was schon fertig kompiliert ist.

    Ich habe mir jetzt folgendermaßen beholfen:
    Im Quellcode überall boost::mutex durch Mutex und boost::mutex::scoped_lock durch ein LOCK() Makro ersetzt.

    Folgenden Header habe ich mir konstruiert:

    #ifdef USE_BOOST_MUTEX
        #include <boost/thread.hpp>
        typedef boost::mutex Mutex;
        #define LOCK(_x) (boost::mutex::scoped_lock(_x))
    #elif USE_QMUTEX
        #include <QMutexLocker>
        typedef QMutex Mutex;
        #define LOCK(_x) (QMutexLocker(&_x))
    #else
        #error No Mutex declaration defined
    #endif
    

    Mit typedefs allein ist es nicht zu machen, da QMutexLocker einen Pointer erwartet, boost aber eine Referenz. Daher das blöde Makro.

    Jetzt kompiliere ich den Krempel zweimal durch und erhalte zwei statische libraries, einmal mit Qt-Mutexen, einmal mit Boost-Mutexen, und die linke ich in meine zwei Applikaitonen.

    Das funktioniert jetzt erstmal.

    Meine Frage belibt trotzdem, ob es keine elegantere Lösung gibt, etwa beide implementierungen zu bauen und beide in eine einzige statische library zu packen, wobei erst beim linken der applikation die eine oder andere object-Datei aus der statischen lib genutzt wird.



  • PhilippM schrieb:

    Folgenden Header habe ich mir konstruiert:

    Achtung, Bezeichner mit führendem Unterstrich sind für den Compiler reserviert.

    Meine Frage belibt trotzdem, ob es keine elegantere Lösung gibt, etwa beide implementierungen zu bauen und beide in eine einzige statische library zu packen, wobei erst beim linken der applikation die eine oder andere object-Datei aus der statischen lib genutzt wird.

    Wenn das irgendwie geht, was ich bezweifle, dann ist es auf jeden Fall vom verwendeten Compiler (bzw. Linker) abhängig. Das hat nicht mehr unbedingt was mit C++ zu tun.

    Das übliche Vorgehen ist es, zwei verschiedene Libs zu bauen. Bei MSVC könntest du dann per Preprocessor die jeweilige Lib auswählen:

    #ifdef USE_BOOST_MUTEX
        #include "boostmutex.h"
        #pragma comment(lib, "boostmutex.lib")
    #elif USE_QMUTEX
        #include "qtmutex.h"
        #pragma comment(lib, "qtmutex.lib")
    #else
        #error No Mutex declaration defined
    #endif
    

    Bei anderen Compilern vermutlich per makefile...



  • Strategypattern?

    Eigentlich würde ich aber auf eine Threadinglibrary setzen, und boost::thread ist da näher am Standard.
    Qt mag auf mehr Plattformen als boost laufen, aber wenn dass wichtig ist, entwickelt man sowieso Qt only.



  • Mach halt deine eigene Scoped-Lock Klasse, die eben NICHT auf Boost/Qt Scoped-Locks basiert, sondern "zu fuss" die entsprechenden Lock/Unlock Funktionen aufruft.

    Oder noch besser: mach deine Mutexen mit den Scoped-Locks von Boost kompatibel, und verwende überall die.


Anmelden zum Antworten