IMPLEMENT_SERIAL und Kein Standardkonstruktor verfügbar



  • Ich versteh es nicht. Habe alles kontrolliert. Der Name den ich bei IMPLEMENT_SERIAL angegeben habe ist genau der meiner Klasse. Er erscheint aber nicht in der Liste.
    Das müsste doch sonst eigentlich schon eine Fehler beim Kompilieren geben oder nicht?



  • Hallo.

    Habe nun zwei fast identische Klassen genommen. Unterschied ist das die eine mehr Membervariablen hat und von der anderen noch weitere Klassen abgeleitet sind.

    Von der einen Klasse lassen sich dynamisch Objekt erzeugen, ist in der Klassenliste vorhanden. Von der anderen lassen sich keine Objekte erzeugen, ist nicht in der Klassenliste vorhanden.

    Woran kann das denn liegen?



  • Hallo Herr Richter.

    Darf ich nochmal was fragen:

    Du schreibst:

    Du könntest ein eigenes System aufbauen, bei denen jedes Objekt seine eigene Factory hat. Jedes Objekt initialisiert sich in dem es (über statische Member) seine Factory im System anmeldet (z.B. in eine Liste oder Map einträgt).

    Hier stellt sich die Frage: wie soll sich das Objekt mit seiner Factory im System anmelden wenn es das Objekt ja noch gar nicht gibt?


  • Mod

    Du erzeugst über ein statisches Factory Objekt eine Liste, die sich selbst durch einen Konstruktor aufbaut. Dasist genau das was auch die MFC macht.



  • Hallo.

    Versteh ich immer noch nicht. Meine Klassen haben nun alle ein statische Methode um sich selber zu kreieren. Diese Funktion soll nun in einer Liste angemeldet werden. Aber wer meldet dies an.

    Ich packe doch in eine map den Klassennamen und die zugehörgige Create-Funktion. Nun muss doch aber jemand den Klassennamen und die Funktion kennen. Und das ist doch nur die Klasse selber. Aber wie will die sich anmelden wenn es sie noch gar nicht gibt.



  • Noch ein Tipp?


  • Mod

    Bau eine verkette Liste.
    Erzeuge für jeden Konstruktor ein statisches Objekt, dass dafür sorgt sich mit der initialisierung in die Liste einzutragen mit ID und Konstruktorfunktion.
    Das geht automatisch!

    Oder Bau Dir simpel selbst einen Array aus "Klassen Token" und statischer Factory Funktion.
    Das muss etwas gepflegt werden...



  • Erzeuge für jeden Konstruktor ein statisches Objekt, dass dafür sorgt sich mit der initialisierung in die Liste einzutragen mit ID und Konstruktorfunktion.

    Dass muss dann ja aber auch immer gepfelgt werden. Ich würde gerne aber ein Makro schreiben, ähnlich dem IMPLEMENT_SERIAL von MFC.

    Dass mir die Createfunkion in der Klasse erstellt und sich gleichzeitig in eine Liste einträgt.



  • Das geht automatisch!
    

    Wie soll das gehen.

    Also wenn eine meiner Klasse so aussieht.

    class CMyClass1 : public CMyClass
    {
      CMyClass1(int var);
      ~CMyClass1();
    
       static CMyClass* CreateObject(int var)
       {
         return new CMyClass(var)
       }
    
    };
    
    std::map<CString,pFnCreate> classmap;
    

    Wie bekomme ich nun die Funktion CreateObject() und den Klassennamen CMyClass1 in die map? Wer ruf hier den insert auf?


  • Mod

    visualer schrieb:

    Das geht automatisch!
    

    Wie soll das gehen.

    Ich habe alles geschrieben was zu sagen ist. Muss man hier immer alles vorkauen bis ins kleinste?

    Das ist jetzt nicht ausgefeiltund einfachmal so gehackt, also bitte nichtall zu viel Kritik.

    BTW: Ich habe nur das MFC Verfahren kopiert....

    // AutoRegister.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include <memory>
    #define CONCAT2(x,y) x##y
    #define CONCAT1(x,y) CONCAT2(x,y)
    #define UNIQUE(x) CONCAT1(x,__COUNTER__)
    
    /////////////////////////////////////////////////////////////////////////////
    //    The base class
    
    class CObjBase
    {
    public:
        CObjBase()
        {
        }
        virtual ~CObjBase()        
        {
        }        
    private:
        CObjBase(const CObjBase &);
        CObjBase &operator=(const CObjBase &);
    };
    
    /////////////////////////////////////////////////////////////////////////////
    //    our factory holder
    
    typedef CObjBase* (*FNOBJECTCREATOR)();
    
    class CFactory
    {
    public:
        CFactory(int iToken, FNOBJECTCREATOR pFnCreate)
            : m_iFactoryToken(iToken)
            , m_pFnCreate(pFnCreate)
        {
    #ifdef _DEBUG
            // Token should not exist twice
            for (const CFactory *pf=m_pFactoryList; pf; pf=pf->m_next)
                _ASSERTE(pf->m_iFactoryToken!=iToken);
    #endif
            m_next = m_pFactoryList;
            m_pFactoryList = this;
        }
        static CObjBase *CreateObject(int iToken)
        {
            for (const CFactory *pf=m_pFactoryList; pf; pf=pf->m_next)
            {
                if (pf->m_iFactoryToken==iToken)
                    return pf->m_pFnCreate();
            }
    
            // Not found
            return NULL;
        }
    
    protected:
        int                m_iFactoryToken;
        FNOBJECTCREATOR    m_pFnCreate;
        const CFactory  *m_next;
    
        static const CFactory *m_pFactoryList;
    };
    
    const CFactory *CFactory::m_pFactoryList = NULL;
    
    /////////////////////////////////////////////////////////////////////////////
    //    Simple macros to implement and create the factories
    
    #define DEFINE_FACTORY()                            \
            public:                                        \
            static CObjBase* CreateObject();
    
    #define IMPLEMENT_FACTORY(classname,iToken)            \
            CObjBase *classname::CreateObject()            \
            {                                            \
                return new classname;                    \
            }                                            \
            static CFactory UNIQUE(sFactory)(iToken,classname::CreateObject);    \
    
    /////////////////////////////////////////////////////////////////////////////
    //    Sample implementation
    
    class CObj1 : public CObjBase
    {
        DEFINE_FACTORY()
    public:
        CObj1()
        {
            puts(__FUNCTION__);
        }
        ~CObj1()
        {
            puts(__FUNCTION__);
        }
    };
    IMPLEMENT_FACTORY(CObj1,1)
    
    class CObj2 : public CObjBase
    {
        DEFINE_FACTORY()
    public:
        CObj2()
        {
            puts(__FUNCTION__);
        }
        ~CObj2()
        {
            puts(__FUNCTION__);
        }
    };
    IMPLEMENT_FACTORY(CObj2,2)
    
    class CObj3 : public CObjBase
    {
        DEFINE_FACTORY()
    public:
        CObj3()
        {
            puts(__FUNCTION__);
        }
        ~CObj3()
        {
            puts(__FUNCTION__);
        }
    };
    IMPLEMENT_FACTORY(CObj3,3)
    
    // Error 
    //IMPLEMENT_FACTORY(CObj3,2)
    
    /////////////////////////////////////////////////////////////////////////////
    //    Sample code
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::auto_ptr<CObjBase> p1(CFactory::CreateObject(1));
        std::auto_ptr<CObjBase> p2(CFactory::CreateObject(2));
        std::auto_ptr<CObjBase> p3(CFactory::CreateObject(3));
        return 0;
    }
    


  • Hallo.

    Erstmal vielen vielen Dank fürs vorkauen.
    Hatte das mit der verketteten Liste nicht verstanden, bzw. noch nie gesehen.

    Funktioniert so weit. Habe nun versucht statt dem int iToken direkt den Klassennamen zu verwenden.

    Also so:

    #define IMPLEMENT_FACTORY(classname)            \
    	CObjBase *classname::CreateObject()            \
    	{                                            \
    		return new classname();                    \
    	}                                            \
    	static CFactory UNIQUE(sFactory)(classname,classname::CreateObject);
    

    Und natürlich alles andere dementsprechend auch angepasst.

    Nun erhalte ich allerdings nen Fehler beim Aufruf des Makros

    Syntaxfehler: Bezeichner 'CreateObject'. Was ist den daran nun falsch?



  • Ok. Natürlich. Habs raus:

    #define IMPLEMENT_FACTORY(classname)            \
        CObjBase *classname::CreateObject()            \
        {                                            \
            return new classname();                    \
        }                                            \
    static CFactory UNIQUE(sFactory)(#classname,classname::CreateObject)
    

    Die Raute machts!!



  • Hallo nochmal.

    Jetzt habe ich noch eine Frage: Ich habe das Beispiel nun auf meine Klassen angewendet. Nun musste ich feststellen, dass die Makros nur dann ausgeführt werden, und somit auch die Initialisierung der statischen Liste nur durchgeführt wird, wenn die Klasse schon einmal verwendet wurde.

    Deshalb hatte ich auch das Problem, dass beim IMPLEMENT_SERIAL von MFC meine Klasse gar nicht in der Liste erschien. Mein Test ergab auch hier: erst wenn die Klasse einmal verwendet wurde, wird auch das Makro ausgeführt und meine Klasse erscheint in der RuntimeClass Liste.

    Woran liegt das?


  • Mod

    Weil der Linker nur die Teile linkt, die auch benötigt werden...
    Alles andere wirft er weg. Wäre ja schlimm wenn es nicht so wäre. 😉

    Du hast wahrscheinlich die statischen Objekte im selben CPP Modul liegen...
    Das geht natürlich nicht, denn ein ganzes Modul wird nicht gelinkt, wnen es nicht benötigt wird. Ist doch auch logisch.

    Packe mal alle statischen Objekte in ein andere CPP Modul, dass garantiert benötigt wird.



  • Du hast wahrscheinlich die statischen Objekte im selben CPP Modul liegen...

    Na die statischen Objekte werden ja durch das Makro hinzugefügt.

    static CFactory UNIQUE(sFactory)(#classname,classname::CreateObject)
    

    Und wenn ich nun eine Klasse habe CObj1 dann habe ich dafür eine Obj1.cpp. Dort wird dann das Makro IMPLEMENT_FACTORY eingefügt und dort befindet sich ja dann auch das statische Objekt. Was will ich da anders machen?


  • Mod

    IMPLEMENT_FACTORY in einem anderen Modul sammeln, dass zwingend gelinkt wird.
    Oder Erzeuge mindestens eine referenz auf ein Objekt von diesen Typen.

    Oder erzwinge durch ein pragma das ein entsprechendes Symbol aus diesem Objekt Modul eingelinkt wird.
    #pragma comment(linker, "/include:forceSomeSymbolTobeIncluded")



  • Danke nochmal für deine Hilfe.

    IMPLEMENT_FACTORY in einem anderen Modul sammeln, dass zwingend gelinkt wird.

    Dann muss ich das irgendwo in eine .cpp packen mit dem das ganze eigentlich gar nichts zu tun hat.

    Oder Erzeuge mindestens eine referenz auf ein Objekt von diesen Typen.

    Ja dann bin ich ja soweit wie zuvor. Dann muss ich ja doch für jede Klasse ein Objekt oder auch nur eine Referenz anlegen und das ganze ist nicht mehr dynamisch.

    #pragma comment(linker, "/include:forceSomeSymbolTobeIncluded")

    Und das muss ich dann für jede meiner Klassen machen.

    Bei allen 3 Möglichkeiten muss ich dann für jede Klasse von der ich ein Objekt dynamisch erzeugen will etwas an einer zentrallen Stelle pflegen. Dann sehe ich aber noch keinen Vorteil zu meinem ursprünglichen switch case oder einer map in der ich auch alle Klassen pflege?

    Seit wann ist das so. Ich habe IMPLEMENT_SERIAL schon mal in einem Projekt verwendet.Und das in VS 2003. Dort musste ich nichts von all dem machen. Nun in VS2008 geht das nicht mehr?

    Gibts da keine andere Möglichkeit?


  • Mod

    Also ich weiß nicht was Du für Probleme hast.

    Ich habe meinen Sample Code von oben auf mehrere CPP Dateien verteilt.
    baseclass.cpp
    obj1.cpp
    obj2.cpp
    obj3.cpp

    Im Main habe eich eine Schleife gebaut, die nur dynamisch die Token-Ids anspricht.

    int _tmain(int argc, _TCHAR* argv[])
    {
    	for (int i=1; i<4; ++i)
    		std::auto_ptr<CObjBase> p1(CFactory::CreateObject(i));
    	return 0;
    }
    

    Ich bekomme sowohl in Debug als auch in der Release Version die korrekte Ausgabe.

    CObj1::CObj1
    CObj1::~CObj1
    CObj2::CObj2
    CObj2::~CObj2
    CObj3::CObj3
    CObj3::~CObj3
    Drücken Sie eine beliebige Taste . . .



  • Also ich weiß nicht was Du für Probleme hast.

    Wenn ich das wüßte würde ich nicht fragen. Keine Ahnung wieso??

    In dem Beispiel geht es, nur nicht in meiner Applikation. Kann es daran liegen, dass das ganze in einer statischen Lib implementiert wurde.


  • Mod

    visualer schrieb:

    In dem Beispiel geht es, nur nicht in meiner Applikation. Kann es daran liegen, dass das ganze in einer statischen Lib implementiert wurde.

    Ja! Und warum verätst Du das erst jetzt, dass Du keine normale Anwednung oder DLL hast? 👎

    Ich habe extra den test gemacht: Statische Objekte aus eoinem Modul in einem Projekt einer EXE/DLL werden automatich eingebunden. Für Code sieht das anders aus.

    Denk mal nach! Meinst Du im ernst jedes Stück Code au einer statischen Lib wird in eine EXE gepackt? Es wird nur das aus einer statischen LIB verwendet was auch benötigt wird.

    Wenn also jedes dieser getrennten Module gelinkt werden soll, dann muss ein Modul aus der statischen Lib geladen werden, dass eben alle diese Module nutzt...


Anmelden zum Antworten