Funktionszeiger aus einer Klassenmethode

  • Vorab - das ganze läuft auf einem Atmel.


    Ich habe eine NTP Klasse erstellt, stellt aus dem Internet die aktuelle Zeit zur Verfügung
    Dazu gibt es die Funktion time_t getNtpTime(); In time_t steht die Zeit im Unix Format zur Verfügung,


    Im Hauptprogramm gibt es eine Lib (time.h) dies ist quasi eine Softwareuhr.
    Dort kann man in der Funktion setSyncProvider( ); einen Funktionpointer einhängen der die time_t zurückgibt und die Uhr ist dann in synch.


    Der Header meiner Klasse schaut so aus:


    Wenn ich im Hauptprogramm sowas mache:


    Code
    TimeServer  InternetZeit;
    ...
     setSyncProvider(InternetZeit.getNtpTime);


    meckert der Compiler das die Pointer nicht kompatibel sind.


    das hier funktioniert ohne Probleme


    Also ist eine Funktonszeiger einer Funkton aus einer Klasse ist was anderes als ein Funktionzeiger aus der aufrufenden Klasse.
    Muss man diese funktion als static defineren ? - nur dann compilert es nicht weil alles static sein muß

    VDR 1 (SD) : ASRock A330 GC, 1 GB RAM, TT- FF Karte rev. 2.3, 7'' TFT, Lirc X10 - Selbstbau Gehäuse - Suse 11.3 (64) vdr-1.7.10 diverse Plugins
    VDR 2 (HD) : MSI G41M-P25, 2 GB RAM, E6700 2x3.20GHz, Gainward GT220, 2TB HD, Lirc X10, TT S2-3600 USB, TT S2-1600, - Suse 11.3 (64) NvidiaTreiber 260.19 vdr-1.7.18 - xineliboutplugin 1.0.90 cvs, xine-lib 1.1.90 , s2-liplianin DVB Treiber

  • Hier mal ein interessantes Tutorial zum Thema Funktionspointer:
    http://www.codeguru.com/cpp/cp…ntertoMember-Function.htm


    Ich würde es, wenn du schon C++ benutzt, allerdings anders machen.
    Definiere eine interface-Klasse mit einer puren, virtuellen Funktion und lasse deine TimeServer-Klasse davon ableiten. Dann kannst du einen Pointer auf ein Objekt übergeben, wo du dann diese Funktion aufrufen kannst.


    In Kurzform:


    setSyncProvider muss sich dann das übergebene Objekt irgendwo merken und kann dann über timeServer->getNtpTime() von dem Objekt die Uhrzeit holen.


    Hoffe, das wird ungefähr klar, wie ich das meine.


    Lars.

  • Ich meine zu verstehen was du meinst.
    Nur dazu müsste ich die Funktion setSyncProvider von time.h äandern ?
    Das will ich aber nicht umbedingt tun, da auch andere Projekte von dieser Lib abhängen.
    So schaut es in der Time.h aus.

    Code
    typedef time_t(*getExternalTime)();
    ...
    void    setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider


    In deinem Link wird erklärt, das ich die funktion als static definieren muss. (wenn ich das richtig begriffen habe)
    Das deckt sich auch mit anderen Lib's die dies tun. Hier eine Ausschnitt aus der DCF Lib, die Funktion getTime(void); wird bei setSyncProvider einhängt.


    Code
    public:	
    	// Public Functions
    	DCF77(int DCF77Pin, int DCFinterrupt, bool OnRisingFlank=true); 
    
    	static time_t getTime(void);
    	static time_t getUTCTime(void);
    	static void Start(void);
    	static void Stop(void);
    	static void int0handler();
     };


    Es reicht wohl nicht, nur die time_t gettime static zu definieren, sondern auch alle anderen Funktionen und Variablen die darauf zugreifen wollen.
    Wenn ich das tue meckert der Compiler z.b das er nicht mehr auf das Array packetBuffer zugreifen kann.
    Ein einfacher Zugriff packetBuffer[x] schlägt dann schon fehl.
    .
    Wenn du Lust hast, kannst ja mal ein Blick auf mein dilettantisches Werk werfen.
    So funktioniert es mit der Wrapper Funktion, ist nur ein schönheitsfehler.
    Da Ich aber was lernen will....

    Dateien

    VDR 1 (SD) : ASRock A330 GC, 1 GB RAM, TT- FF Karte rev. 2.3, 7'' TFT, Lirc X10 - Selbstbau Gehäuse - Suse 11.3 (64) vdr-1.7.10 diverse Plugins
    VDR 2 (HD) : MSI G41M-P25, 2 GB RAM, E6700 2x3.20GHz, Gainward GT220, 2TB HD, Lirc X10, TT S2-3600 USB, TT S2-1600, - Suse 11.3 (64) NvidiaTreiber 260.19 vdr-1.7.18 - xineliboutplugin 1.0.90 cvs, xine-lib 1.1.90 , s2-liplianin DVB Treiber

  • Ah, ok, setSyncProvider ist nicht von dir, sondern extern. Alles klar. Ich schau mir das nachher mal an.
    Im Zweifelsfall und wenn du sowieso nur eine Instanz deiner Klasse brauchst, kannst du darin natürlich alles static machen, auch die Felder. Leider scheint man auch keinen void* als userdata übergeben zu können, was normalerweise üblich ist.


    Ich melde mich nachher noch mal.


    Lars

  • Du kannst nicht einfach eine normale, nicht-statische, Methode einer Klasse als Funktionszeiger verwenden. Nicht-statische Methode sind an die Objektinstanz gebunden und die ist nicht an die Methode gebunden.


    Ist nicht ganz einfach zu formulieren.


    Anders gesagt: Innerhalb der Methode hast du indirekt Zugriff auf die member-variable deines Objektes, direkt kann man das mit dem this-pointer machen und der muss ja irgendwo herkommen.


    Dein Beispiel mit globalen Funktion ist uerbigens identisch mit der Implementation einer statischen Methode in deiner Klasse: du brauchst eine Objektinstanz.


    Wie ich oben sagte, es nicht ganz einfach, aber nicht unmoeglich: ich finde allerdings keine deutschen Tutorials: Eine Suche mit "class member method as C function pointer" ergibt einige Resultate.

  • Du kannst nicht einfach eine normale, nicht-statische, Methode einer Klasse als Funktionszeiger verwenden.


    Doch, das geht, siehe das Tutorial, das ich verlinkt habe. Die Syntax ist bloß etwas gewöhnungsbedürftig.
    Hier geht es aber auch darum, den Pointer an eine C-Lib zu übergeben und nicht C++, deshalb ist es etwas schwieriger und ich weiß auch noch nicht, ob es überhaupt geht.


    Hatte noch keine Zeit, in den Code zu gucken.


    Lars.

  • Wie sieht "gettimefunction" eigentlich im Header aus?


    Edit: Hab's gefunden: https://github.com/PaulStoffre…me/blob/master/Time.h#L61
    Edit 2: Ach, hattest du ja auch oben gepostet...


    Lars.

  • In diesem Fall scheint es besser zu sein, mit einer static-Funktion zu arbeiten.
    Als Workaround kannst du in der Klasse TimeServer noch ein static-member hinzufügen:


    Du musst dann nur darauf achten, dass du _server evtl. noch mit einer Mutex schützt und dass dein TimeServer Objekt lang genug lebt, solange die Funktion aufgerufen wird.


    Das ist dann sowas wie ein Singleton-Pattern.


    Lars.

  • Hallo Lars, danke für deine Mühe.



    Der Compiler meckert beim Erzeugen der Instanzen udp und eth und auch beim definieren von packetBuffer.
    Wenn ich dies static mache wird dies akzeptiert.


    Alle in der cpp müssen dann alle Zugriffe auf die member Funktionen von TimeServer via _server-> erfolgen, was auch auch teilweise zum Erfolg führt.
    So richtig verstehe ich die Zusammenhänge nicht mehr wirklich, wir bewegen uns glaube ich auch in einem C++ Bereich der für != C++Profis nicht ganz einfach ist.


    Im Arduino Forum habe ich diese Frage auch schon gestellt, aber wenn es um OOP geht gibt es nicht viel Leute die da eine Antwort parat haben.
    Und für dich es auch schwierig, wenn du die Entwicklungsumgebung incl. der verwendeten Lib nicht hast.


    Nur wenn du viel Lust und Zeit hast, in der DCF77 Lib wird dies gemacht was ich vor habe aber bei mir nicht hinbekomme - kannst ja mal reinschauen.
    Dort wird alles static definiert.


    Grüße Rudi

    Dateien

    VDR 1 (SD) : ASRock A330 GC, 1 GB RAM, TT- FF Karte rev. 2.3, 7'' TFT, Lirc X10 - Selbstbau Gehäuse - Suse 11.3 (64) vdr-1.7.10 diverse Plugins
    VDR 2 (HD) : MSI G41M-P25, 2 GB RAM, E6700 2x3.20GHz, Gainward GT220, 2TB HD, Lirc X10, TT S2-3600 USB, TT S2-1600, - Suse 11.3 (64) NvidiaTreiber 260.19 vdr-1.7.18 - xineliboutplugin 1.0.90 cvs, xine-lib 1.1.90 , s2-liplianin DVB Treiber

  • Moin!


    Ich hab mal "meine" Änderungen eingebaut, diesmal mit einer zusätzlichen Funktion, damit es evtl. klarer wird.
    Keine Ahnung, ob es kompiliert, müsste es aber eigentlich. :)


    Lars.

  • Moin, Lars


    Es kompiliert und funktioniert.
    Ich hatte gestern wohl das Prinzip nicht verstanden .
    Hatte gerade den Aha Effekt ..... Ahhh so wird ein Schuh draus.


    Musste zwar noch dies ändern.


    Code
    private: 
      TimeServer* _server; 
    ....


    in

    Code
    private: 
     static TimeServer* _server; 
    ....


    Dann funktionierte es.
    Auf diese Lösung wäre ich nie gekommen.
    Ich gehe aber schon davon aus das du Berufsmässig mit Softwareentwicklung zu tun hast ?


    Grüße Rudi


    PS: Ich darf deine Lösung schon im Arduino Forum posten ? Natürlich mit den Hinweiß das es dein Werk war.

    VDR 1 (SD) : ASRock A330 GC, 1 GB RAM, TT- FF Karte rev. 2.3, 7'' TFT, Lirc X10 - Selbstbau Gehäuse - Suse 11.3 (64) vdr-1.7.10 diverse Plugins
    VDR 2 (HD) : MSI G41M-P25, 2 GB RAM, E6700 2x3.20GHz, Gainward GT220, 2TB HD, Lirc X10, TT S2-3600 USB, TT S2-1600, - Suse 11.3 (64) NvidiaTreiber 260.19 vdr-1.7.18 - xineliboutplugin 1.0.90 cvs, xine-lib 1.1.90 , s2-liplianin DVB Treiber

  • Stimmt, da muss ein static hin, hab das nur so nebenbei getippt. :)


    Ich gehe aber schon davon aus das du Berufsmässig mit Softwareentwicklung zu tun hast ?


    Hatte letztes Jahr 20-jähriges Dienstjubiläum, in Wirklichkeit sind es ein paar Jahre mehr. Sind schon fast 30, naja, 25 vielleicht. Wann kam der C64 raus?

    PS: Ich darf deine Lösung schon im Arduino Forum posten ? Natürlich mit den Hinweiß das es dein Werk war.


    Posten darfst du es gerne, aber ich muss nicht unbedingt erwähnt werden. Das ist nichts herausragendes, sondern eigentlich eine Standardlösung, die man häufig benutzt, wenn man aus C++ heraus in einer C-Lib einen Callback hinterlegen muss.
    Häufig gibt es dann noch einen "void* userdata" Parameter, wo man dann das TimeServer-Objekt hätte übergeben können. Im Callback würde man dann diesen Pointer einfach auf die Klasse casten und könnte es dann benutzen.


    Stell dir vor, es würde so aussehen:


    Sowas wird dir bestimmt auch noch irgendwo begegnen.
    Dann könnte man auch mit mehreren TimeServer-Objekten arbeiten.


    Lars.

Jetzt mitmachen!

Sie haben noch kein Benutzerkonto auf unserer Seite? Registrieren Sie sich kostenlos und nehmen Sie an unserer Community teil!