C-Funktion mit FunKtionspointer als Parameter in einer C++ Klasse aufrufen.



  • Hallo,

    ich habe eine C-Bibliothek um die ich eine C++ Klasse bauen möchte.
    In meiner Klasse möchte ich eine C-Funktion aufrufen die als Parameter einen Funktionszeiger void (*p_handler) (uint32_t, uint32_t) entgegennimmt.

    Als Funktionszeiger kann ich nun eine statische Methode meiner Klasse oder eine Lambda Funktion übergeben. Soweit habe ich dies auch schon gemacht und funktioniert auch.

    Jetzt habe ich aber das Problem, dass ich mehrere Objekte von meiner Klasse erzeugen möchte, dadurch muss ich in meiner statischen Methode bzw Lambda Funktion auf das Objekt zurückgreifen können.

    Mit den zwei Parametern die beim Funktionsaufruf übergeben werden, kann ich das Objekt wieder identifizieren. Deshalb fällt mir momentan nur ein, dass ich die Pointer der erzeugten Objekte in einer linked list speichere und dann daraus das zugehörige Objekt raussuche.

    Oder gibt es hier eine komplett andere Lösung zu meinen Problem?

    Hinweis:
    Die Objekte werden nicht zur Laufzeit durch new oder sonstiger Speicherbeschaffung erzeugt.

    godi


  • Mod

    Welche Bedeutung haben die beiden Parameter? Kann man die irgendwie beeinflussen? Deine Beschreibung klingt so, als ob dies so wäre. Kannst du das näher erläutern?

    Du musst eben irgendwie Information über das aufrufende Objekt an die Callbackfunktion übergeben bekommen. Üblicherweise haben Callbacks oft einen void-Zeiger als Parameter, über den man das machen könnte. Dieser aber offensichtlich nicht. Daher wäre es gut zu wissen, was die beiden ints für eine Bedeutung haben.



  • Hallo,

    danke für deine Antwort.

    SeppJ schrieb:

    Welche Bedeutung haben die beiden Parameter? Kann man die irgendwie beeinflussen? Deine Beschreibung klingt so, als ob dies so wäre. Kannst du das näher erläutern?

    Die C-Bibliothek greift auf eine Hardware zu. Mit den beiden Parametern wird die Hardware identifiziert. Also die werden in der Funktion wo der Funktionspointer übergeben wird auch übergeben. Wenn ein Interrupt von der Hardware kommt wird die übergebene Funktion aufgerufen und die Parameter wieder mit zurückgegeben, so dass erkannt werden kann von welcher Hardware der Interrupt kommt.

    SeppJ schrieb:

    Du musst eben irgendwie Information über das aufrufende Objekt an die Callbackfunktion übergeben bekommen. Üblicherweise haben Callbacks oft einen void-Zeiger als Parameter, über den man das machen könnte. Dieser aber offensichtlich nicht. Daher wäre es gut zu wissen, was die beiden ints für eine Bedeutung haben.

    Ja dieser Callback hat leider keinen void-Pointer. Die beiden Integer sind, wie oben beschrieben, zur Identifizierung der Hardware. Also da kann ich leider nichts "willkürliches" vorgeben.


  • Mod

    godi schrieb:

    SeppJ schrieb:

    Du musst eben irgendwie Information über das aufrufende Objekt an die Callbackfunktion übergeben bekommen. Üblicherweise haben Callbacks oft einen void-Zeiger als Parameter, über den man das machen könnte. Dieser aber offensichtlich nicht. Daher wäre es gut zu wissen, was die beiden ints für eine Bedeutung haben.

    Ja dieser Callback hat leider keinen void-Pointer. Die beiden Integer sind, wie oben beschrieben, zur Identifizierung der Hardware. Also da kann ich leider nichts "willkürliches" vorgeben.

    Dann dürftest du mit standardkonformen Mitteln am Ende sein. Wenn es keine Möglichkeit gibt, irgendwie Information an die Funktion zu übergeben, dann muss die Information in der Funktion selber liegen. Was mir dazu spontan einfiele, wäre tatsächlich neue Instanzen der Funktion im Datensegment zu erzeugen (jeweils mit passender Information) und diese aufzurufen, aber das wäre weder Standard noch würde das überhaupt auf einigen gängigen Systemen funktionieren.

    Dein Eingangsbeitrag besagt, dass du bereits eine Lösung hast, weil die Funktion dann wohl doch nicht ganz beliebig ist, sondern sich anhand der beiden ints identifizieren lässt? Ich verstehe zwar nicht den Sinn dahinter, ausgerechnet eine linked list zu benutzen (eine sehr praxisferne Datenstruktur), aber an sich hört es sich nach einer brauchbaren Idee an. Wenn die Schnittstelle keine Weitergabe von Information anbietet, dann ist das eben einfach so und man muss sich irgendwie damit arrangieren. Es gibt keine Zaubertricks, diese Beschränkung zu umgehen.



  • Vielen Dank für deine Antwort,

    die linked list ist mir nur spontan eingefallen, da ich den Head als static definieren kann und wenn mein erstes Objekt erzeugt wird, dann wird hier der this-Pointer gespeichert. Bei weiteren Objekten wird dann der Head immer auf das aktuelle Objekt gesetzt und der alte Head auf den Pointer für das nächste Element.
    Die STL muss ich mir erst genauer ansehen und versuchen diese auf meine Platform zu bringen. Vor allem das Speichermanagement.

    Zum finden des passenden Objektes muss ich dann wohl und übel über die linked list iterieren und auf die zwei Parameter vergleichen. Da es sich aber nicht um viele Objekte (~10) handelt, sehe ich das auch nicht so schlimm.

    Aber für eine andere Lösung bin ich natürlich offen! 🙂



  • Wenn die maximale Anzahl der Objekte bekannt ist reicht auch ein Array. Die Luxuslösung wäre wohl eine std::map/std::unordered_map , aber je nachdem wo deine Software läuft ist das eventuell übertrieben. Also auf einem Mikrocontroller mit wenig RAM und Speicher wohl eher das Array oder die Linked List.



  • Werde es mit einer linked list umsetzen.

    An eine unsorted_map habe ich auch schon gedacht, jedoch wird diese nicht standardmäßig unterstützt, also da muss ich noch herausfinden wie ich die auf meinem System (µC und kein zu kleiner 😉 ) zum laufen bringe, die ich diese eventuell eh benötige.

    Aber danke für eure Antworten. 🙂


  • Mod

    Was soll den hier eine linked list? Das einzige wozu list gut ist, ist wenn man massiv viele splice-Operationen machen will. Was hier offensichtlich nicht der Fall ist. Ansonsten ist das eine solch schreckliche Datenstruktur, dass ich mir selbst bei massiven splice-Anforderungen doppelt und dreifach überlegen würde, ob ich eine list möchte.



  • SeppJ schrieb:

    Was soll den hier eine linked list? Das einzige wozu list gut ist, ist wenn man massiv viele splice-Operationen machen will. Was hier offensichtlich nicht der Fall ist. Ansonsten ist das eine solch schreckliche Datenstruktur, dass ich mir selbst bei massiven splice-Anforderungen doppelt und dreifach überlegen würde, ob ich eine list möchte.

    Neulich 300fach gemessen. 🕶
    Man überlege es sich 300 mal. Oder so.


  • Mod

    volkard schrieb:

    Neulich 300fach gemessen. 🕶
    Man überlege es sich 300 mal. Oder so.

    Dermatologisch geprüft?



  • @godi
    Klingt zwar so als ob du das Thema für dich abgeschlossen hättest, aber weil ich es irgendwie interessant finde, und ein paar weitere Anmerkungen zu dem habe was bereits geschrieben wurde... schreib ich die einfach mal.

    Die Sache mit array/vector/map/unordered_map sollte mehr oder weniger klar sein. Ist quasi die Standardlösung für dieses Standardproblem (ja, kommt leider öfter vor als man meint - gibt viel zu viele grottige Libs ohne "userdata" Parameter).
    Dabei möchte ich bloss noch das Wort "thread safety" erwähnen. Falls deine Plattform Threads unterstützt, musst du dich darum kümmern dass das Eintragen und Nachgucken in der Datenstruktur thread safe gemacht wird.

    Bzw. noch schlimmer wäre...

    Wenn ein Interrupt von der Hardware kommt wird die übergebene Funktion aufgerufen und die Parameter wieder mit zurückgegeben, so dass erkannt werden kann von welcher Hardware der Interrupt kommt.

    ...falls die Funktion direkt aus dem Interrupt aufgerufen wird. Dann wird es etwas haarig. Wobei du dann sowieso auch sehr aufpassen musst was du in der Funktion machst. So "harmlose" Sachen wie Speicher anfordern können dir dabei schon das Genick brechen.

    ----

    Was den Vorschlag "Instanzen der Funktion im Datensegment erzeugen" angeht...
    Das ist natürlich die Variante mit dem geringsten Overhead. Falls du in die Richtung gehen willst dann such mal nach dem Begriff "thunk". Dabei kopierst du nicht die ganze Funktion, sondern erstellst bloss ein "Trampolin" im Datensegment, welches einen zusätzlichen Parameter auf den Stack pusht (oder in ein Register lädt), und dann einen JMP (keinen CALL!) zur Adresse der eigentlichen Callback-Funktion macht.
    Der Wert des Parameters ist dabei als Konstante im Code des "Thunk" enthalten.

    ----

    Weitere Möglichkeit: falls die Objekte immer nur mit Konstanten für die Device-ID instanziert werden, könntest du einfach Templates machen. Da jedes Template seine eigene Instanz der Callback-Funktion bekommt, weiss jeder Callback dann auch für welche Template-Instanz er aufgerufen wurde.

    ... = new Device(123, 456);
    ==>
    ... = new Device<123, 456>();
    


  • Danke für eure Antworten.

    Ich habe es mittlerweile anders gelöst, da mir der Source Code von der Bibliothek vorliegt, habe ich einen zusätzlichen void * Parameter hinzugefügt, damit ich den Pointer des jeweiligen Objektes übergeben kann.

    @hustbaer:
    Der Ansatz mit den Tamplates finde ich auch interessant, werde ich mal testen wenn ich mehr Zeit habe.


Anmelden zum Antworten