Hi Leute,
ich benötige einen Funktionszeiger, der auf Funktionen zeigen kann, die
entweder 2 oder 3 Argumente haben.
Habe es schon so versucht, geht aber nicht.
void (*ArgFu_pf)(uint8 value1, ...);
Es wird durch eine weitere Abfrage sichergestellt, dass die Funktion
immer richtig aufgerufen wird, allerdings erst zur Laufzeit.
Wie muss ich den Zeiger schreiben, damit dies Funktioniert?
Thomas Frosch schrieb:> Hi Leute,>> ich benötige einen Funktionszeiger, der auf Funktionen zeigen kann, die> entweder 2 oder 3 Argumente haben.>> Habe es schon so versucht, geht aber nicht.>> void (*ArgFu_pf)(uint8 value1, ...);
Ähm nein.
Das ist ein Pointer auf eine variadische Funktion. D.h. die Funktion
selber muss auch variadisch sein. Du kannst diesem Pointer nicht einmal
eine Funktion mit 2 Argumenten und ein anderes mal mit 3 Argumenten
zuweisen.
So spielt es das nicht.
> Wie muss ich den Zeiger schreiben, damit dies Funktioniert?
Du musst dein Konzept ändern.
3 Typedefs machen, einen generischen Funktionspointer und die beiden
konkreten Varianten. Speichern dann als generischen Pointer, und beim
Aufruf einen Type-Cast benutzen.
Thomas Frosch schrieb:> Könntest du das genauer ausführen? Typ-Cast hört sich zwar sehr gut an,> aber wie muss ich die deklaration des Zeigers machen?
Genauso wie jetzt auch.
Nur dass du typedef davor schreibst und der 'Variablenname' damit zum
Namen deines neuen Datentyps wird.
typedef int (* IntFnct)( long arg1, double arg2 );
IntFunc ist ein neuer Datentyp (so wie auch int oder double ein Datentyp
ist), der ein Funktionspointer ist auf eine Funktion mit genau dieser
STruktur.
Und so wie du mit
int i;
eine Variable i vom Typ int anlegst, legst du eben mit
IntFnc funcPtr;
eine Variable namens funcPtr an, die vom Datentyp "Pointer auf Funktion,
die einen long und einen double nimmt und einen int retourniert" an.
Hallo,
geht das ? Ist auch noch typesicher.
typedef void (*tfnp2) (int,int);
typedef void (*tfnp3) (int,int,int);
union vproc {
tfnp2 p2;
tfnp3 p3;
};
typedef union vproc tfnpx;
void fn2 (int a, int b) {
}
void fn3 (int a, int b,int c) {
}
int main(int argc, char* argv[]) {
tfnpx pp;
pp.p2 = fn2;
pp.p2(1,2);
pp.p3 = fn3;
pp.p3(1,2,3);
return 0;
}
Stefan
Was heißt 2 oder 3 Argumente haben? Interessant wäre, ob die besagte
Funktion neben den 2/3 übergebenen Argumenten auch welche zurückliefert.
Davon ausgehend, das sie einen int zurückliefert und 2 int als argumente
erwartet, sähe es dann so aus:
1
int(*funktionspointer)(int,int);//Deklariert den Funktionspointer
Obigen Code habe ich eben mal schnell aus dem Gedächtnis in der Mensa
getippt, keine Garantie auf Funktionstüchtigkeit, aber sofern ich die
Informatik-Vorlesung noch richtig in Erinnerung habe, sollte das
Grundkonzept so funktionieren.
P.s. weil ich's grad sehe...klar gibt die Funktion nur einen Wert
zurück, wüsste nicht das es in C etwas anderes gäbe
edit und gut ja, c hätte man auch per Referenz zurückgeben können aus
der Funktion, aber ist jetzt nebensächlich
Markus Burger schrieb:> Was heißt 2 oder 3 Argumente haben?> ...> Obigen Code habe ich eben mal schnell aus dem Gedächtnis in der Mensa> getippt, keine Garantie auf Funktionstüchtigkeit, aber sofern ich die> Informatik-Vorlesung noch richtig in Erinnerung habe, sollte das> Grundkonzept so funktionieren.
Du hast die Frage nicht richtig verstanden. Er möchte einen
Funktionszeiger haben, der sowohl auf Funktionen mit 2 Argumenten, als
auch auf Funktionen mit 3 Argumenten zeigen können soll.
So wird im ASF vom AVR32 auf ISR-Funktionen gesprungen.
Ist ne Klasse Methode, um Devicetreiber von abstrahierter Funktionalität
zu trennen.
Beispiel:
UART-Empfangen, um Datenstrom nach Kommandos zu durchsuchen. So etwas
wie ein Befehlsinterpreter. Befehle interpretieren brauncht nicht
zwingend mit einer UART oder sonst irgendeiner
Kommunikationsschnittstelle verbunden werden.
Die Komponente Befehlsinterpreter ist es gleich, von woher die Daten der
UART kommen.
Man kann so, relativ unkompliziert von der Hardware unabhängig
Funktionen programmieren.
Der Clou:
Benutzt man Funktionspointer im RAM, kann man während der Laufzeit die
Pointer ummappen. Das sogenannte "late binding". Und das geht sogar auf
dem AVR-8Bitter. Ich habe so mein Mini-OS programmiert.
Wer das nicht will, legt die Pointervariablen in den Flash und nutzt nur
die syntaktischen Vorteile während des Builds.
Stefan schrieb:> Hallo,>> geht das ?
Ja ist ok.
Wenn du jetzt noch eine Struct drüberlegst, in der du noch einen Member
hast, der dir sagt, welcher der beiden Pointerdatentypen der richtige
ist, dann ist es richtig typsicher und die verwendende Stelle braucht
noch nicht einmal wissen, welcher der beiden Pointer es ist, weil sie es
in den Daten bestimmen kann
#define ARG2 0
#define ARG3 1
struct fnct
{
uint8_t type; // 0 = 2 Argumente, 1 = 3 Arumgente
vproc fncPtr;
}
void foo( struct fnct* Function )
{
if( Function->type == ARG2 )
Function->fncPtr.p2( 2, 3 );
else if( Funtion->type == ARG3 )
Function->fncPtr.p3( 2, 3, 4 );
}
Sorry, checks nicht mit der typedef Variante. Ich weiss zwar was typedef
macht etc, aber wie ich das nun genau schreiben muss bekomme ich nicht
hin.
Habe ... versucht.
Es tritt aber anscheinend ein Speicherzugriffsproblem auf. Ich teste im
moment auf einem Windows Rechner. Später kommt das auf nen µC. Ich teste
einfach Codeschnipsel in einer Dialogbasierten Win32 Anwendung.
Programm läuft durch und macht die Ausgabe. Dann wird angezeigt, dass
die .exe ein Problem festgestellt hat und beendet werden muss.
Thomas Frosch schrieb:> Sorry, checks nicht mit der typedef Variante. Ich weiss zwar was typedef> macht etc, aber wie ich das nun genau schreiben muss bekomme ich nicht> hin.
Zeig mal dein komplettes, compilierbares Testbeispiel
Karl Heinz Buchegger schrieb:> Thomas Frosch schrieb:>> Sorry, checks nicht mit der typedef Variante. Ich weiss zwar was typedef>> macht etc, aber wie ich das nun genau schreiben muss bekomme ich nicht>> hin.>> Zeig mal dein komplettes, compilierbares Testbeispiel
Argh, zu spät gelesen
> Ich teste einfach Codeschnipsel in einer Dialogbasierten Win32 Anwendung.
Irgendwo hast du etwas falsch gemacht. Ist aber mit diesen Schnipseln
nicht erkennbar.
Stefan Ernst schrieb:> Markus Burger schrieb:>> Was heißt 2 oder 3 Argumente haben?>> ...>> Obigen Code habe ich eben mal schnell aus dem Gedächtnis in der Mensa>> getippt, keine Garantie auf Funktionstüchtigkeit, aber sofern ich die>> Informatik-Vorlesung noch richtig in Erinnerung habe, sollte das>> Grundkonzept so funktionieren.>> Du hast die Frage nicht richtig verstanden. Er möchte einen> Funktionszeiger haben, der sowohl auf Funktionen mit 2 Argumenten, als> auch auf Funktionen mit 3 Argumenten zeigen können soll.
Okay ja da hast du Recht, Missverständnis :)
Oh ihr habt recht, als ich das File nun sauber gemacht habe, um es
hochzuladen ist der Fehler verschwunden.
Funktioniert jetzt!
Allerdings muss ich es jetzt noch hinbekommen, den cast wegzubekommen
bzw schon vorher festzulegen.
Ich habe eine Konfigurationsstruktur, die ich dann initialisiere. Der
jenige der diese Struktur initialisiert soll nicht jedesmal den cast
davor schreiben müssen. Gibt es da nun auch noch irgendeine Möglichkeit?
Also an die Stelle "element" soll in der Konfiguration einfach ein
Funktionszeiger auf eine Funktion mit 3 oder 2 Argumenten hinkommen.
Thomas Frosch schrieb:> Allerdings muss ich es jetzt noch hinbekommen, den cast wegzubekommen> bzw schon vorher festzulegen.
Scroll ein bischen hoch.
Da hab ich mal skizziert, wie man das Typsicher macht, wenn man noch ein
paar Setzfunktionen zum Befüllen der Struktur mit dazunimmt.
> Ich habe eine Konfigurationsstruktur, die ich dann initialisiere. Der> jenige der diese Struktur initialisiert soll nicht jedesmal den cast> davor schreiben müssen. Gibt es da nun auch noch irgendeine Möglichkeit?
Schreib dir Funktionen dafür. Das hat dann auch den Vorteil, dass der
Compiler überwacht, ob du auch die richtige Funktion in die struct
stopfst oder nicht.
Es wird eine Datei geben, in der ein Array dieser Struktur drin ist.
Dort wird dann zig mal irgendwelche Funktionszeiger eingetragen. Später
eventuell sogar mit Konfigurationstool.
Will ein Programmieren anderer total verhindern. Es sollen alle
Einstellungen rein in der Struktur geschehen.
Dein Beispiel mit der Union finde ich sehr gut, jedoch funktioniert das
Initialisieren nicht so toll.
1
CanIf_RxPduConfig_STypecanIf_rxPduConfig_s
2
{
3
0,
4
&ArgFu2
5
6
};
Funktioniert! Aber nur mit Fehlermeldung, dass Klammern fehlen.
1
CanIf_RxPduConfig_STypecanIf_rxPduConfig_s
2
{
3
1,
4
&ArgFu3
5
};
Funktioniert nicht. Er meckert wieder, wegen unterschiedlicher
Argumentenzahl. Ich müsste also irgendwie angeben, dass er dies in p3
schreiben soll. Dann kann ich aber auch wieder zwei getrennte Variablen
nehmen.
Was ich noch sagen wollte.
In solchen Fällen pfeift man auch oft auf die Unterschiede und definiert
ganz einfach, das alle derartigen Callback-Funktionen die gleiche
Argumentliste haben müssen. Dann machen halt einige Funktionen nix mit
ihren übergebenen Werten, was solls. Die Unterscheidung, welche Funktion
dann tatsächlich aufgerufen werden soll, kostet auch Zeit, so dass das
übergeben von ein paar Werten an eine Funktion, die nix damit macht real
gesehen auch nicht langsamer ist.
Dafür muss man aber keine Cast-Orgien veranstalten und auch sonst keine
Sonderfälle bauen und der Compiler kann alles soweit überprüfen, dass
man keinen Fehler machen kann.