Jein.
Funktionen mit unterschiedlichen Argumenten, also unterschiedliche
Funktionstypen, sind nicht kompatibel. Jenachdem kann man sie casten,
aber das ist UB.
Historisch hatten in C Funktionen nicht immer Argumente. Deshalb muss
man für Argumentlose Funktionen (void) für die Argumente schreiben. Aber
ohne das ist der Funktion prototyp auch valide, und kompatibel zu
Funktionstypen mit Argumenten. Folgendes ist valides C:
1
typedefint(*prototype_t)();
2
3
intfoo(intx){returnx;}
4
intbar(intx,inty){returnx+y;}
5
6
intmain(){
7
prototype_ta=foo;
8
a=bar;
9
a(1,2);
10
}
Es gibt aber glaube ich trotzdem gewisse Limitationen, was für
Funktionstypen erlaubt sind, usw.
Riecht aber sehr nach XY-Problem.
Bei Funktionspointern sollten die Argument Typen fest stehen.
Immerhin weiss ja der Aufrufer normalerweise nicht auf welche Funktion
der Pointer nun wirklich zeigt, bzw. sollte es ihm egal sein.
>> zu deklarieren, um sowohl auf foo als auch boo zeigen zu können? oder
Geht, in dem Sinne dass der Compiler keinen Fehler wirft. Was dann das
Programm macht, lese eine beliebige Einführung in "undefined behaviour"
und warum man das nicht möchte.
> wie geht man da am besten vor?
Man lässt es.
Was möchtest Du mit einem Pointer, der mal auf eine void foo(int) zeigt
und mal auf eine void boo(int, int)?
Doch vermutlich die Funktionen aufrufen. Damit das überhaupt
funktionieren kann, müsstest Du wissen, wohin der Pointer gerade zeigt.
Dann brauchst Du den Pointer nicht.
Vermutlich möchtest Du eine Form von Late Binding erzielen. Solange Du
in C bleibst, sorge dafür, dass alle Funktionen, die über den Zeiger
aufgerufen werden, die gleiche Signatur haben. Nur das funktioniert.
Falls Du mehr willst, must Du C++ machen. Dann hast viel mehr
Möglichkeiten, Dir selbst ins Knie zu schießen :-)
zu gast schrieb:> um sowohl auf foo als auch boo zeigen zu können
Das zeigen ist ja meistens nicht das Problem. Sondern das korrekte
aufrufen. Meistens passiert in C nix. Aber sobald ein Parameter manchmal
größer ist als int, braucht er in jedem Fall die konkrete Form.
Und das nix passiert, heisst nix. Also, wie rufst Du das auf? Ggf Union.
zu gast schrieb:> Ursprünglich hatte ich vor den Pointer mit einem enum zusammen in einer> Struktur zu halten als Information, was jetzt erwartet wird.
Das Ganze wirkt einfach unausgegoren. Funktionspointer machen keinen
Sinn, wenn man sich erst mal zusammensuchen muss, WAS genau man
übergeben muss.
Das Gegenteil ist der Fall. Funktionspointer nimmt man dann, wenn die
Parameter immer gleich sind, die Funktionalität sich aber Ändern soll
ohne dass der Aufrufer etwas darüber wissen muss.
Also nochmal 5 Schritte zurück, Sinn und Zweck überlege. WAS will man
erreichen. Dann noch mal WIE erreicht man das am saubersten.
zu gast schrieb:> Ursprünglich hatte ich vor den Pointer mit einem enum zusammen in einer> Struktur zu halten als Information, was jetzt erwartet wird.
Dann ist es am Besten, die unterschiedlichen Funktionszeigertypen per
union abzulegen und über die enum zu entscheiden, welcher verwendet
wird. Du darfst natürlich nicht den einen Funktionszeigertyp zuweisen
und dann den anderen auslesen.
1
enumCallType{Unary,Binary};
2
structCall{
3
enumCallTypetype;
4
union{
5
int(*unary)(int);
6
int(*binary)(int,int);
7
};
8
};
9
10
voidcall(structCall*c){
11
switch(c->type){
12
caseUnary:
13
c->unary(1);
14
break;
15
caseBinary:
16
c->binary(1,2);
17
break;
18
}
19
}
In C++ kannst du das mit std::variant sicherer und sauberer machen.
A. S. schrieb:> zu gast schrieb:>> um sowohl auf foo als auch boo zeigen zu können>> Das zeigen ist ja meistens nicht das Problem. Sondern das korrekte> aufrufen.
Das stimmt in diesem Fall, aber nicht generell (z.B. Pointerarithmetik).
A. S. schrieb:> Meistens passiert in C nix.
Das ist eine sehr gefährlicher Satz.
>Das Ganze wirkt einfach unausgegoren.
Ich muss gestehen, dass mir das inzwischen selber so vorkommt. Aber
dennoch ist der Thread bis jetzt für mich wirklich lehrreich und
hilfreich. Vielleicht ergeben sich noch andere Aspekte und ich werde
sowiso noch mal ein paar Rollen rückwärts machen.
zu gast schrieb:> Ursprünglich hatte ich vor den Pointer mit einem enum zusammen in einer> Struktur zu halten als Information, was jetzt erwartet wird.
Wenn das enum den Selektor darstellt, welcher der Prototypen gemeint
ist, dann ist der Funktionszeiger kein Funktionszeiger, sondern ein
union.
Dafür sind unions gemacht.
zu gast schrieb:> Ist es generell möglich so etwas wieint (*pfunc)(int, ...)>> zu deklarieren, um sowohl auf foo als auch boo zeigen zu können? oder> wie geht man da am besten vor?
Nein. Es ist nicht möglich.
Das Problem liegt darin, wie die vararg liste aufgebaut ist. Das ist ABI
abhängig und kann ggf. massiv explodieren.
Beispielsweise werden bei manchen Architekturen die ersten paar
Funktionsargumente nicht auf den Stack gelegt, sondern in Registern
übergeben, um die Performance und Stacknutzung zu optimieren. Da ist es
bspw. möglich, dass die ersten 4 Argumente in Registern R0 bis R3
liegen.
Schreibt man nun eine vararg Funktion
1
voidfunc(int,int,...);
Varargs sind aufgrund ihrer dynamischen Größe leider schlecht auf
Register zu mappen. Deswegen passiert häufig folgendes Mapping: Die
beiden integer am Anfang landen in R0 und R1 und der Rest kommt auf den
Stack, wohingegen die Funktion
1
voidfunc(int,int,int);
alle Parameter in die register R0, R1 und R2 pressen kann.
Alle Angaben sind hier natürlich spekulativ und hängen konkret vom
Compiler + Architektur ab -> ABI.
Ein weiteres Problem der vararg Liste ist ihre Unfähigkeit floats zu
handhaben. Beispiel:
1
// printf(const char *format, ...);
2
3
floatmy_float=2.4f;
4
printf("Float is: %f\n",my_float);
die float Variable my_float wird hier in der vararg Liste übergeben.
Gemäß des C-Standards erfolgt nun hier die automatische double-promotion
und der float wird in einen double umgewandelt. (Ja, das ist immer so!
Das kann man auch nicht abstellen)
Dieses Verhalten würde deinen Funktionspointer-Casts ebenfalls das
Genick brechen.