Forum: PC-Programmierung [c++] Funktor statt Funktionspointer übergeben


von Vlad T. (vlad_tepesch)


Lesenswert?

Hi,
Ich habe eine Schnittstelle, die ein Funktionspointer erwartet.

Gibt es eine Möglichkeit, statt eines Funktionszeigers an diese Funktion 
einen Funktor zu übergeben (ohne diese Schnittstelle zu ändern)?

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

was ist denn der Unterschied zwischen Funktor und Funktionspointer?

von Vlad T. (vlad_tepesch)


Lesenswert?

kennst du google?

von Εrnst B. (ernst)


Lesenswert?

Evtl. mittels Template-Adapter-Funktion, die den Funktor ausführt, und 
als Funktionspointer weitergereicht werden kann...

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Nein, anstelle eines Funktionspointers dürfte sich kein Functor 
übergeben lassen, da dieser aus mehr als nur einer Adresse besteht. Da 
aber ein Funktionspointer zur Laufzeit nichts anderes ist als eine 
Adresse, geht jeder Objektbezug verloren und muss "von Hand" an anderer 
Stelle mitgeführt werden. Aus diesem Grund weisen viele C-APIs an den 
Stellen, an denen sie Funktionspointer erwarten, die Möglichkeit auf, 
einen opaken Pointer auf irgendwas mitzugeben, der der aufgerufenen 
Funktion wiederum mitgegeben wird.
Dieser opake Pointer eignet sich dazu, um z.B. den /this/-Pointer eines 
Objektes zu transportieren, ist aber natürlich von allen 
Typüberprüfungen ausgenommen, was die ganze Angelegenheit etwas, nun, 
fehlerträchtig machen kann. Bei ausreichender Disziplin seitens des 
Programmierers aber spricht nichts gegen die Verwendung derartiger 
Konstrukte.

von Vlad T. (vlad_tepesch)


Lesenswert?

Εrnst B✶ schrieb:
> Evtl. mittels Template-Adapter-Funktion, die den Funktor ausführt, und
> als Funktionspointer weitergereicht werden kann...

meinst du so:
1
void template<class Func> wrapFunctor(int i)
2
{
3
  Func f;
4
  f(i);
5
}

da fehlt mir aber dann das konkrete Objekt.
oder wie meinst du was anderes?

mein Problem ist, dass ich beim Aufruf der Funktion bestimmte 
Zusatzinformationen brauch, die ich vorher bei Konstruktion des Funktors 
übergebe.

das ganze wird benötigt, weil ich ein Stück Code wrappen möchte.
In der wrappenden Umgebung sind aber zusatzinformationen bei Ausführung 
notwendig, die in der Originalumgebung nicht gebraucht werden, weswegen 
es dort eine zustandslose Funktion auch getan hat.

von Vlad T. (vlad_tepesch)


Lesenswert?

Rufus Τ. Firefly schrieb:
> [...]
Danke für deine Erläuterungen

Je mehr ich darüber nachdenke, desto mehr bin ich auch der Meinung, dass 
es nicht funktioniert.
Der einzige Weg, der mir einfällt wie man sowas von Sprachseite umsetzen 
könnte, würde nur auf einer von-Neumann-Architektur funktionieren

von Karl H. (kbuchegg)


Lesenswert?

Wenn der von Rufus skizzierte Weg nicht geht, geht immer noch der 
klassische Weg, auch wenn er nicht schön ist.

Der Schnittstelle gibst du einen Pointer auf eine freistehende Funktion. 
Dein 'arbeitendes Objekt' wird als globales (oder global statisch) 
ausgeführt (oder gleich als Pointer, wies dir eben besser passt) und die 
freistehende Funktion gibt dann an das Objekt weiter. So kann man die 
Kurve kratzen, um aus einer Funktionsumgebung wieder in eine 
Objektumgebung zu kommen.
1
static MyClass* worker;
2
3
static void callBackFnct()
4
{
5
  (*worker)();
6
}
7
8
...
9
10
   worker = new MyClass( was auch immer das Objekt zur Arbeit braucht );
11
   API_fnct( callbackFnct );
12
   delete worker;

Aber über eine Schnittstelle, die einen Funktionspointer haben will, 
kriegst du nun mal kein Objekt drüber. Da beisst die Maus keinen Faden 
ab.

von Εrnst B. (ernst)


Lesenswert?

Vlad Tepesch schrieb:
> Der einzige Weg, der mir einfällt wie man sowas von Sprachseite umsetzen
> könnte, würde nur auf einer von-Neumann-Architektur funktionieren

Du wolltest die Funktion umkopieren, und jeweils eine eigene Kopie pro 
Funktor verwenden?

Könnte gehen, aber inzwischen versuchen auch die 
Von-Neumann-Architekturen ihr Erbe loszuwerden... DEP, NX, und wie die 
ganzen Zusatz-Sicherheitsfeatures aktueller CPUs heissen...

Vlad Tepesch schrieb:
> meinst du so:
> void template<class Func> wrapFunctor(int i)
> {
>   Func f;
>   f(i);
> }

Genauso hatte ich das gedacht... in einem "qsort"-Beispiel-Fall ginge 
das auch...


Ansonsten, wenn du keine Threads hast, könntest du mit einem globalen 
Funktor-Pointer arbeiten, den die Callback-Funktion benutzt.

Mit Threads ginge das auch, müsstest dich dann aber selber um 
TLS-Variablen kümmern (pthread_setspecific, TlsAlloc, ...)

Trotzdem bleiben einige Nebenbedinungen, wie z.B. kein verschachtelter 
Aufruf der Callback-Funktion, Aufruf immer aus demselben Thread der den 
Pointer an die API übergeben hat usw.

von Kan a. (Firma: Basta) (kanasta)


Lesenswert?

um auf die ursprüngliche Frage zurückzukommen: ich denke, nein.

von Rolf Magnus (Gast)


Lesenswert?

Vlad Tepesch schrieb:
> da fehlt mir aber dann das konkrete Objekt.

Das Problem wirst du immer haben, wenn du nur einen Funktionszeiger 
übergibst.

Vlad Tepesch schrieb:
> mein Problem ist, dass ich beim Aufruf der Funktion bestimmte
> Zusatzinformationen brauch, die ich vorher bei Konstruktion des Funktors
> übergebe.

Ah, das ist also das, was du eigentlich willst. Da wird dein 
Lösungsansatz dich aber nicht weiterbringen. In deinen Funktionszeiger 
kannst du nunmal keine Zusatzinformationen mit reinstecken, und 
Funktionsobjekte (was du "Funktor" nennst) funktionieren ganz anders und 
können nicht per Funktionszeiger übergeben werden.

> das ganze wird benötigt, weil ich ein Stück Code wrappen möchte.
> In der wrappenden Umgebung sind aber zusatzinformationen bei Ausführung
> notwendig, die in der Originalumgebung nicht gebraucht werden, weswegen
> es dort eine zustandslose Funktion auch getan hat.

Da wurd dann wohl zu kurz gedacht. Eigentlich schreibt man solche 
Callback-Sachen doch immer so, daß ein zusätzlicher Zeiger auf void 
übergeben wird, mit dem der Benutzer des APIs machen kann, was er will.
Ich denke, da wird man sich igendeinen unschönen Work-Around basteln 
müssen, aber wie der aussieht, hängt davon ab, in welchem Kontext das 
benutzt wird.

von Vlad T. (vlad_tepesch)


Lesenswert?

Rolf Magnus schrieb:
> Eigentlich schreibt man solche
> Callback-Sachen doch immer so, daß ein zusätzlicher Zeiger auf void
> übergeben wird, mit dem der Benutzer des APIs machen kann, was er will.
> Ich denke, da wird man sich igendeinen unschönen Work-Around basteln
> müssen, aber wie der aussieht, hängt davon ab, in welchem Kontext das
> benutzt wird.

Ich hab eine Lösung für mein Problem gefunden.

tatsächlich sieht der Funktionspointer so (ähnlich) aus:
typdef void (*SendFunc)(Data*);

der gerufene Code versendet für den aufrufenden Code das Datum.
in der Wrapperumgebung ist das send aber etwas komplexer, so dass hier 
Infos zum aufgerufenen Wrapper benötigt werden.

da ich in der glüchlichen Situation bin, dass ich den aufrufenden Code 
vorher den Puffer, den er beschreiben soll, mitteile kann ich
Variante A: eine Zuordnung Puffer-Pointer->Wrapper (zB über std::map) 
machen. In der statischen CallBack kriegt man so das Objekt.
oder
Variante B:
eine Struktur von Data ableiten, mit Zusatzinformationen (wrapper-this) 
versehen und dem aufrufenden Code geben. die Statische CallBack kommt 
dann per downcast an die Zusatzdaten (den Wrapper-Pointer)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.