Problem mit dynamischen Fenstern



  • Ja, du brauchst die Methode TCreateForm::CreateKomp<T> nicht, dazu ist ja das Template CreateKomp<T> da. Abgesehen davon ist die Methodensignatur falsch, bei allen Methode, die einen Template Parameter erwarten, musst du ein template<typename T> voranstellen (T kann natürlich durch jeden anderen gültigen Namen ersetzt werden).

    // non-member template Funktion
    template<typename T>
    void func()
    {
       ...
    }
    
    // member template Funktion
    class Test
    {
       template<typename T>
       void func()
       {
         ...
       }
    };
    


  • Sorry, ich hatte ein bißchen schlampig die Template-Funktion hingeschrieben, aber habe es nun korrigiert.
    Diese (freie) Funktion mußt du dann in einen Header packen oder aber zu Beginn deines Quelltextes.

    Benutzung dann folgendermaßen:

    TLabel *label = Creata<TLabel>(this, this, 10, 10, 100, 16, "Hello World!");
    label->Color = clRed;
    

    Der Rückgabewert ist, wie schon geschrieben, der Zeiger auf die Control-Instanz, so daß du dann weitere Eigenschaften setzen oder Methoden dafür aufrufen kannst.
    Um die Freigabe brauchst du dich aber nicht kümmern (außer du übergibst 0 als ersten Parameter).

    Alternativ könntest du diese Funktion auch in ein Basisformular als Methode packen (und deine eigenen Formulare erben dann hiervon) - dann könntest du auf den ersten Parameter 'Owner' verzichten und direkt 'this' einsetzen.



  • Hallo,
    vielen Dank für deine Antwort. Das Implementieren klappt soweit, doch eine Hürde kann ich leider noch nicht überwinden. Ich möchte, dass deine Methode Member einer Klasse wird. Meine Klassendeklaration sieht soweit so aus:

    class TCreateForm
    {
     private:
      int width,
          height,
          left,
          top;
      TColor clBack;
      TForm *form; //die zu erstellende Form
    
     public:
      TCreateForm(int width, int height, int left, int top, TColor clBack, String cap);
      ~TCreateForm();
    
      template <typename T>
      T* CreateKomp(TWinControl *Owner, TWinControl *Parent, int left, int top, int width, int height, String caption);
    
      void DestroyKomp();
    
    };
    

    Mein Aufruf lautet:

    TCreateForm f(300,300,500,600,clHotLight,"C++ ist cool"); //funktioniert
     f.CreateKomp<TLabel>(f,f,100, 100, 300, 106, "Hello World!"); //funktioniert nicht --> ich übergebe f, da ich das Label auf diesem Formular haben möchte
    

    Das Problem ist nun, dass ich folgende Fehlermeldung bekomme:

    Keine Übereinstimmung für 'TCreateForm::CreateKomp<T>(TCreateForm,TCreateForm,int,int,int,int,char *)' gefunden

    Die Fehlermeldung ist mir schon irgendwie klar, da als Parameter ja ein Formular gefordert wird, doch ich nun ein Objekt des Types TCreateForm übergebe. Wie kann ich dieses Problem lösen? Ich habe bis jetzt die zu erstellende Form als private Member. Muss meine Klasse etwa von TForm abgeleitet sein oder wie muss ich vorgehen?
    Vielen Dank
    lg, freakC++



  • Hast dus mal so versucht?

    f.CreateKomp<TLabel>(&f,&f,100, 100, 300, 106, "Hello World!");
    


  • Hallo Braunstein,
    nein, das funktioniert nicht. Das kann ja auch gar nicht laufen, da die Instanz f vom Typ TCreateForm ist.

    Hat jemand eine Idee, was ich hier machen muss. Ich probiere mal von TForm abzuleiten....

    Vielen Dank
    lg, freakC++

    edit: Der Versuch von TForm abzuleiten bringt mich hier auch nicht weiter.



  • TCreatForm soll deine Form doch nur erzeugen und nicht besitzen. Deine neue Form kann darauf ja auch nicht dargestellt werden (Parent) da TCreateForm ja nicht von TWinControl abgeleitet ist. Willst du das überhaupt?
    Wenn TCreateForm deine Form nur erzeugen aber nicht darstellen soll übergib doch die Zeiger von der Form in der dein Objekt dargestellt werden soll oder 0 wenn du es noch nicht weißt.



  • Hallo,
    ja Du hast recht. Die Klasse TCreateForm soll nur das Formular erstellen und auf dieses dann Komponenten setzen. Die zu erstellende Form ist ja als private Member deklariert. Wie soll ich denn jetzt von außen einen Zeiger darauf übergeben??

    Ich verstehe zwar, was Du meinst, doch könntest Du mal ein bisschen Code zeigen. Das würde mich sehr helfen.

    Vielen Dank für dein Bemühen.
    lg, freakC++



  • Wenn die zu erstellende Form eine Membervariable deiner Klasse ist, dann brauchst die Parent und Owner ja nicht mehr an CreateKomp zu übergeben.
    Wie ich sehe übergibst du ja auch schon die Position und die Caption für die neue Form im Konstruktor von TCreateForm. Dann übergibst du alles noch mal an CreateKomp.
    So wie das aussieht sollte CreateKomp eigentlich eine freie Funktion sein. Wenn du die unbedingt in einer Klasse halten willst dann kannst du auch die membervariablen dieser Klasse verwenden.
    z.Bsp. so:

    class TCreateForm
    {
     private:
      int width,
          height,
          left,
          top;
      TColor clBack;
      String caption;
      TForm *form; //die zu erstellende Form
      TForm* parent;
    
     public:
      TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap);
      ~TCreateForm();
    
      template <typename T>
      void CreateKomp();
    
      void DestroyKomp();
    
    }; 
    
    template<typename T>
    void TCreateForm::CreateKomp<T>()
    {
    // Initialisierung erfolgt hier mit den Membervariablen deiner Form
     T *t = new t(parent);
     t->SetBounds(left, top, width, height);
     t->Parent = parent;
     t->Caption = caption;
     form = t;
    }
    

    Du solltest hier aber besser gleich deine Klasse zum Template machen statt nur die Funktion.
    Nochmal die Frage. Warum eine Klasse und nicht eine freie Funktion?



  • Hallo Braunstein,
    vielen Dank für den Code. Also ich möchte eine Klasse erstellen, weil diese ja verschiedene Aufgaben übernehmen soll. Über den Konstruktor wird dynmaisch ein neues Formular angelegt. Mittels der Methode CreateKomp soll dann auf dieses neue Formular beliebige Komponenten, wie TLabel, gesetzt werden. Ich kann die Klasse nicht ganz zum Template machen, da ja anfangs immer ein Fomular erstellt werden soll und dann beliebig andere Komponenten. Sonst müsste ich immer wieder den KOnstruktor aufrufen.

    Bei deinem Code ist mir jedoch noch eine Sache unklar:

    TCreateForm f(f,300,500,600,clHotLight,"Form Caption");
    

    Solch eine Definition funktioniert leider immer noch nicht. Damit sollte nun eigentlich der Konstruktor aufgerufen werden und ein Formular erstellt werden.

    Der Konstruktor sieht so aus:

    TCreateForm::TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap)
    {
     Application->CreateForm(__classid(TForm), &form);
     form->Show();
     form->Width = width;
     form->Height = height;
     form->Left = left;
     form->Top = top;
     form->Color = clBack;
     form->Caption = cap;
    }
    

    Weißt Du da auch bescheid 🙂 ?
    Danke!
    lg, freakC++



  • Ach so. Da habe ich dich vorher missverstanden.
    Ich würde das dann so machen.

    TCreateForm::TCreateForm(TForm* parent, int width, int height, int left, int top, TColor clBack, String cap)
    {
     form = new TForm(0); // Wir brauchen hier keinen Owner da wir im Destruktor selber löschen können
     form->Parent = parent;
     form->Width = width;
     form->Height = height;
     form->Left = left;
     form->Top = top;
     form->Color = clBack;
     form->Caption = cap;
     form->Show();
    }
    

    Die Erzeugung könnte dann so laufen.

    TCreateForm f(parentForm,300,500,600,clHotLight,"Form Caption");
    

    parentForm ist dann ein Zeiger auf die Form in der deine Form dargestellt wird oder 0 falls deine Form ein eigenes Fenster ist.



  • Hallo Braunstein,
    ich habe deinen Code mal umgesetzt und das dynamische Formular wird nun auch erstellt, doch nun möchte ich ja auch die beliebige Komponente auf diesem Formular erstellen,. Ich muss also die CreateKomp - Methode abändern, da ich sie Parameter benötigt (bei dir hätte sie die gleiche breite, höhe etc. wie das erstellte Formular).

    template<typename T>
    void TCreateForm::CreateKomp<T>(int left, int top, int width, int height, String cap)
    {
     T *t = new t(form); //durch Konstruktor dynamisch erstelltes Formular 
     t->Parent = form;
     t->Show();
     t->SetBounds(left, top, width, height);
     t->Caption = caption;
    }
    

    So würde dann der Aufruf von CreateKomp aussehen:

    TCreateForm f(0,500,300,400,400,clHotLight,"Form Caption"); //funktioniert
     f.CreateKomp<TLabel>(100, 100, 300, 106, "Hello World!"); //funktioniert leider noch nicht :(
    

    Ich bin mir jetzt nicht sicher, ob ich genügend Parameter habe. Eigentlich sollte nun ein Label mit der Aufschrift "Hello World!" auf dem gerade erzeugten Formular erscheinen. Das tut es aber nicht. Es kommt ein Linkerfehler:

    Unresolved external 'void TCreateForm::CreateKompStdctrls::TLabel(int, int, int, int, System::AnsiString)' referenced from ...

    Ich glaube, dass da irgendwas mit den parents etc. nicht stimmt. Warum ist das alles für mich so viel komplizierter, wenn ich das in eine Klasse packe? 🙄

    Vielen Dank
    lg, freakC++



  • Hallo

    Kann es sein, das du die Template-Methode in CreateForm.h deklariert hast, im CreateForm.cpp implementiert hast und der Aufruf f.CreateKomp<TLabel>( in einer anderen Unit-Implementation steht, die nur Createform.h included? Wenn ja : Bei Templates muß immer die ganze Implementation zugänglich sein, nicht nur die Deklaration. Deshalb must du auch die Implementation der Template-Methode in den Header CreateForm.h verschieben.

    bis bald
    akari



  • Hallo akari,
    jaaa, daran hat es gelegen! Vielen, vielen Dank! Nun funktioniert alles so wie ich es haben möchte. Ist das bei templates allgemein so? Wenn die Funktionen dann aber schon in der Klassendeklaration implementiert sind, sind sie dann nicht automatisch inline?

    Ach, was würde ich geben, um euer Wissen zu haben 🙂

    Bis zum nächsten Thread!

    lg, freakC++



  • Hallo

    Ist das bei templates allgemein so?

    Ja, das ist bei allen Template-Funktionen so. Egal ob Member einer Klasse oder nicht.

    Wenn die Funktionen dann aber schon in der Klassendeklaration implementiert sind, sind sie dann nicht automatisch inline?

    Ja, das macht die Template-Methode automatisch inline. Templates müßen immer inline verfügbar sein, das folgt aus ihrer Funkionalität.

    bis bald
    akari



  • Hallo,
    achso! Das ist gut zu wissen. Was wäre denn, wenn sie nicht inline wären?

    Vielen Dank
    lg, freakC++



  • Hallo

    Wenn beim konkreten Einsatz eines Templates die Implementation des Templates fehlt, kommt der Linker-Fehler den du ja schon kennst. Der Compiler kann nämlich erst beim konkreten Einsatz des Templates aus der Implementation des Template eine konkrete Funktion zusammensetzen, nach Übersetzung von Markos und Templates darf es nur noch exakt festgelegte Datentypen geben. Wenn du also dein CreateKomp-Template in deinem gesamten Projekt für insgesamt 3 verschiedene Datentypen spezifizierst, sind nach dem Kompilieren dann drei verschiedene Funktionen vorhanden, und das Template ist verschwunden.
    Kann der Compiler aber das deklarierte Template für einen konkreten Einsatz nicht auflösen kann weil die Implementation fehlt, merkt das der Linker und bricht ab. (Warum nicht gleich der Compiler den Fehler findet und abbricht ist mir jetzt auch nicht klar)

    bis bald
    akari



  • akari schrieb:

    Kann der Compiler aber das deklarierte Template für einen konkreten Einsatz nicht auflösen kann weil die Implementation fehlt, merkt das der Linker und bricht ab. (Warum nicht gleich der Compiler den Fehler findet und abbricht ist mir jetzt auch nicht klar)

    Weil du etwa eine explizite Template-Spezialisierung durchaus in einer anderen Quelldatei unterbringen kannst.



  • Hallo,
    ja, das klingt logisch. Ich verstehe dann nur nicht, warum ich die template Funktionen nicht mit dem Schlüsselwort "inline" versehen kann und sie dann auch in die .cpp Datei schreiben kann.

    edit: Mir ist noch eine Frage aufgekommen, die ich euch gerne stellen möchte.

    Wenn ich nun auf mein dynamisches Forumal ein Label setzen, dann kann ich darauf zugreifen, indem ich den Rückgabetyp von CreateKomp nutze. Also so:

    TLabel *label = f.CreateKomp<TLabel>(10,10,f.width-40,200,"");
     label->AutoSize = false;
    

    Wenn ich nun jedoch auf das Formular zugreifen möchte, dann habe ich ein Problem. Dieses erstelle ich nämlich im Konstruktor:

    TCreateForm f (0,Form1->Width/2,Form1->Height/2,400, 350, clHighlight, "Hello");
    

    Da ein Konstruktor keinen Rückgabetyp hat, kann hier nicht so arbeiten wie bei CreateKomp. Wie könnte ich dennoch auf das dynamisch erstelle Formular zugreifen? Führt nichts um eine Methode herum, die das Formular erstellt? Ich finde es nämlich schöner, wenn die Form im Konstruktor gemacht wird.

    Vielen Dank für eure Hilfe
    lg, freakC++



  • ...gelöscht...



  • Hallo

    freakC++ schrieb:

    Hallo,
    ja, das klingt logisch. Ich verstehe dann nur nicht, warum ich die template Funktionen nicht mit dem Schlüsselwort "inline" versehen kann und sie dann auch in die .cpp Datei schreiben kann.

    Soweit ich weiß, sollte laut C++ Standard mit dem Schlüsselwort export genau das funktionieren, aber die meisten Compiler (auch der von Borland) unterstützen das nicht. Für weitere Fragen zum Hintergrund solltest du aber besser mit einem neuen Thread im C++ Forum selber nachfragen.

    Da ein Konstruktor keinen Rückgabetyp hat, kann hier nicht so arbeiten wie bei CreateKomp. Wie könnte ich dennoch auf das dynamisch erstelle Formular zugreifen? Führt nichts um eine Methode herum, die das Formular erstellt? Ich finde es nämlich schöner, wenn die Form im Konstruktor gemacht wird.

    Doch ein Konstruktor hat einen Rückgabetyp, nämlich die erstellte Instanz. Ich verstehe dein Problem nicht. In deinem Beispiel-Code erstellt du zunächstmal keine dynamische Instanz, sondern eine statische (in der Variablen f). Und warum du f nicht nutzen können solltest oder warum du eine Funktion haben willst, verstehe ich auch nicht.

    bis bald
    akari


Anmelden zum Antworten