problem mit Callback



  • hallo leute

    folgendes problem. ich habe mir ein projekt mit 2 Forms erstellt und moechte nun, das die 2te form der ersten form die adresse einer funktion uebergibt, die die erste aufrufen soll wenn sie mit einer bestimmten arbeit fertig ist.

    leider funktioniert es nicht, wenn ich die funktionen als member der class TForm2 deklariere.

    // Unit1.h
    
    #ifndef Unit1H
    #define Unit1H
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    //---------------------------------------------------------------------------
    
    typedef void myfunc();
    
    class TForm1 : public TForm
    {                                                                (1)
    __published:	// Von der IDE verwaltete Komponenten
            TEdit *Edit1;
            TButton *Button1;
            void __fastcall Button1Click(TObject *Sender);
    private:	// Anwender-Deklarationen
    public:		// Anwender-Deklarationen
            __fastcall TForm1(TComponent* Owner);
            void back(void (*fnc)());
            void back2(void (__closure *func1)());
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm1 *Form1;
    //---------------------------------------------------------------------------
    #endif
    
    // Unit1.cpp
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    #include "Unit2.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
            : TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    void TForm1::back(void (*fnc)())
    {
      fnc();
    }
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
      Form2->Show();
    }
    //---------------------------------------------------------------------------
    void TForm1::back2(void (__closure *func1)())
    {
      func1();
    }
    
    // Unit2.h
    
    #ifndef Unit2H
    #define Unit2H
    //---------------------------------------------------------------------------
    #include <Classes.hpp>
    #include <Controls.hpp>
    #include <StdCtrls.hpp>
    #include <Forms.hpp>
    //---------------------------------------------------------------------------
    class TForm2 : public TForm
    {
    __published:	// Von der IDE verwaltete Komponenten
            TEdit *Edit1;
            TButton *Button1;
            TButton *Button2;
            void __fastcall Button1Click(TObject *Sender);
            void __fastcall Button2Click(TObject *Sender);
    
    private:	// Anwender-Deklarationen
    public:		// Anwender-Deklarationen
            __fastcall TForm2(TComponent* Owner);
            void f3();
            void (__closure *f4)();
    };
    //---------------------------------------------------------------------------
    extern PACKAGE TForm2 *Form2;
    //---------------------------------------------------------------------------
    #endif
    
    // Unit2.cpp
    
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit2.h"
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm2 *Form2;
    
    void f1();
    void (*f2)();
    
    //---------------------------------------------------------------------------
    __fastcall TForm2::TForm2(TComponent* Owner)
            : TForm(Owner)
    {
      f2=&f1;
      f4=&f3;
    }
    //---------------------------------------------------------------------------
    
    void f1()
    {
      ShowMessage("Callbackfunction wurde aufgerufen");
    }
    void __fastcall TForm2::Button1Click(TObject *Sender)
    {
      Form1->back(f2);
    }
    //---------------------------------------------------------------------------
    
    void __fastcall TForm2::Button2Click(TObject *Sender)
    {
      Form1->back2(f4);
    }
    //---------------------------------------------------------------------------
    
    void TForm1::f3()
    {
      ShowMessage("Member-Callbackfunction wurde aufgerufen");
    }
    

    nach dem versuch den code zu kompilieren kommt immer der fehler:

    Zugriffsverletzung bei Adresse 00C3F749 in Modul 'COMP32P.DLL'. Schreiben von Adresse 08020030

    danach bleibt er dann bei (1) stehen.

    kann mir da vielleicht jemand weiter helfen. hab mit der suchfunktion schon nachgeguckt, aber da is weniger was dabei, was mir helfen hat koennen.

    Meep Meep



  • Warum willst Du denn solche Konstruktionen einsetzen? Wenn Deine Form2 auf die Klasse Form1 zugreifen will, gibt es die Möglichkeit, friends zu verwenden. Das widerspricht allerdings dem Kapselungsgedanken...

    Oder willst Du damit eine Art Vererbungs emulieren?



  • hallo JFK

    noe. ich will ne Form bauen, die von vielen anderen formen aufgerufen wird bauen. dabei geht es darum, das in der form bestimmte daten eingegeben werden muessen. auch werden von den aufrufenden Forms noch parameter uebergeben.

    ich dachte mir, das es am besten waere, wenn man die form aufruft, ihr die parameter und dann noch die adresse einer funktion weiter gibt, die dann von der aufgerufenen Form aufgerufen wird. hmm ob das nun verstaendlich war ???

    auf jeden fall moechte ich das als callback realisieren. ansonsten muesste ich immer wieder fuer die form neue headers von anderen zukuenftigen forms einbinden.

    hast du vielleicht ne ahnung wo der fehler liegen koennte ?

    Meep Meep



  • Das CALLBACK-Zeugs haste von der WinAPI auf C++ adaptiert. Das ist so aber gar nicht nötig.

    Eine Klasse kapselt ja bestimmte Dinge. Wenn Du also eine TForm-Klasse "X" definierst, mußt Du Dir im Vorfeld überlegen, wer die Klasse alles verwenden will und welche Zwecke X dabei abdecken muß. Wenn Du das weißt, dann definierst Du Deinen Konstruktor. Hast Du z.B. viele Werte zu Übergeben, bieten sich Strukturen oder ggf. sogar Datenklassen an. Du hast dabei die Möglichkeit mehrere Konstruktoren zu definieren (nennt sich "überladen"). Z.B. einen Standardkonstruktor, der gar keine Parameter hat und einen, der 10 Parameter benötigt.

    Mit "public" und "private" hast Du die Möglichkeit, Methoden in X zu definieren, die von anderen Anwendern gesehen werden können oder nicht. So kannst Du auch Daten veröffentlichen und verstecken.

    Wenn nun die andere TForm-Klasse "A" die TForm-Klasse X verwenden will, legt sie zur Laufzeit eine TForm-Klasse X Instanz an und nutzt die Klasse. Das IncludeFile von A includiert die Deklarationen von X und gut ist. Im IncludeFile von X brauchst Du gar nicht wissen, wer X aufrufen wird, weil das der X Klasse vollkommen wurscht ist!
    Das Callback-Gekruschtel brauchst Du da also überhaupt nicht.

    Und wenn Deine X einmal zu klein wird und noch Erweiterungen braucht, kannst Du X2 von X ableiten und dadurch die Funktionalität von X in X2 erweitern. Alte Anwender erreichen immer noch ihre "X", neue Anwender können jedoch X2 verwenden und kann es wiederum auch wurscht sein, daß X2 auf X aufbaut.

    Zu dem Thema solltest Du Dir mal die Hilfetexte zu C++ anschauen und ein gutes Buch zulegen.



  • das ich das nur mit den klassen und ohne callback machen kann, war mir schon bewusst. ich hab mir allerdings ueberlegt, das ich die eine Form dann in eine dll packe, und da wird es mit nem callback etwas ´besser sein.
    ich bin halt noch am lernen. irgendwie kommt es mir mit den callbacks hmm eleganter vor.

    hab es bis jetzt immer nach deinem ansatz gemacht. bin aber irgendwie nicht so wirchtig gluecklich damit geworden.

    der callback funktioniert jetzt uebrigens.

    was spricht eiendlich deiner meinung nach gegen callbacks ?

    Meep Meep



  • Die Programme werden unübersichtlich, wenn man wild in diversen Klassen herumspringt. Ereignisbehandlungsroutinen verwenden im Prinzip auch ein Callback. Wenn man aber komplexere Oberflächen hat, wird es da manchmal schon ein wenig unüberschaubar, wer wann wo auf welche Daten zugreift. Insbesondere dann, wenn eine Ereignisbehandlungsroutine am Zug ist und während ihrer Laufzeit (indirekt!) weitere Ereignisbehandlungsroutinen aufruft, die ggf. Daten in der gleichen Klasseninstanz verändern können. Das sind manchmal ziemlich bescheuerte Fehlersuchen, weil die Fehler dann auch gerne nur ab und an auftauchen...

    Ich habe mal eine Art TStringGrid Komponente geschrieben, die einen Ausschnitt aus einer beliebigen Datenmenge anzeigen kann. Die anzuzeigende Daten werden vom Klassenanwender via Ereignisbehandlungsroutine bereitgestellt. Da macht das Sinn, weil ich als Klasse gar keine andere Möglichkeit habe, an meinen Anwender "heranzukommen", wenn ich etwas brauche (es sei denn, ich verwende ein langsames und umständliches Messagesystem).

    Aber in dem von Dir geschilderten Fall halte ich das eigentlich für unnötig. Welchen Vorteil hast Du denn von Callback?



  • ich hab z.b. eine form x, die ich fuer eine simple datenabfrage auf einem anderen server verwende.
    diese form x benoetige ich in sehr vielen forms, und werde sie auch in zukunft noch in vielen anderen forms verwenden muessen. die form x ist in sich schon fertig (weshalb ich sie auch dann mal in eine dll packen moechte).
    wenn nun eine neue form dazu kommt, die die form x auch braucht, dann binde ich die form x in die neue ein. danach ruf ich eine bestimmte funktion der form x auf, uebergebe ihr meine funktionsaddy und eine struktur mit parametern.
    danach erhalte ich ueber den callback eine neue struktur mit daten.

    ich finde das prinzip in diesem fall angenehm und ohne viel arbeitsaufwand.

    Meep Meep


Anmelden zum Antworten