Forum: Compiler & IDEs Pointer auf Funktionen mit unterschiedlicher Anzahl von Parametern


von Uwe B. (boerge) Benutzerseite


Lesenswert?

MoinMoin,

ich habe da mal eine C-Problem, welches mich gerade beschäftigt, wobei 
ich aber nicht weis, ob es dafür eine Lösung gibt, vielleicht hat ja 
hier jemand eine Idee:

Man kann ja, nach der bekannten Methode, Funktionen über Pointer 
aufrufen. Bei der Typ-Deklaration solcher Funktionspointer muss man, 
wenn diese Funktionen Parameter haben, laut "Lehrbuch", die Typen der 
Übergabeparameter mit deklarieren. Soweit so gut und kein Problem, wenn 
die so verpackten Funktionen alle die gleiche Anzahl von 
Übergabeparametern haben und die Typen ebenfalls gleich sind. Damit kann 
man dann ja auch, wie ich das auch möchte, eine schicke Tabelle mit 
allen Funktionspointern usw. aufbauen....

Mein Problem: die Anzahl der Übergabeparameter und deren Typ ist bei mir 
nicht gleich! Erschwerend kommt dazu, dass ich nicht in jeder Funktionen 
eine Mimik ala va_list, va_start, va_arg, va_end einbauen möchte, also 
ich sie unverändert lassen möchte (weil sie halt gegeben sind und u.U. 
im Programm auch ohne Funktionspointer aufgerufen werden sollen usw.).

Frage, gibt es einen Weg dieses Problem irgendwie doch zu lösen!

Grüße & Danke Uwe

von Nobody (Gast)


Lesenswert?

Dann mach wrapper-Funktionen mit va_.

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

> Mein Problem: die Anzahl der Übergabeparameter und deren Typ ist bei mir
> nicht gleich!

Dann hast du ein Problem

> Erschwerend kommt dazu, dass ich nicht in jeder Funktionen
> eine Mimik ala va_list, va_start, va_arg, va_end einbauen möchte,

musst du auch nicht.
main() weiß auch nicht, wieviele Argumente es bekommt und hat trotzdem 
einen Mechanismus wie es eine variable Argumentliste bekommt.

> also
> ich sie unverändert lassen möchte (weil sie halt gegeben sind und u.U.
> im Programm auch ohne Funktionspointer aufgerufen werden sollen usw.).

Du könntest

* Zwischenwrapper einbauen.
  Also Funktionen, die alle die gleiche Argumentliste haben. Ihre 
einzige
  Aufgabe ist es, sich aus der fixen Argumentliste die benötigten
  herauszuholen und damit die eigentlichen Funktionen aufzurufen

* Casten was das Zeug hält.
  Zu deinen Funktionspointern kommt noch ein zusätzliches Attribut dazu,
  das dir sagt, von welchem konkretem Typ der Funktionspointer ist.
  (Du hast also einen Vorrat an verschiedenen Funktionspointer-
  Datentypen und dieser Eintrag sagt dir welcher der richtige ist)
  Dort siehst du nach, castest den Pointer zurecht und rufst die
  Funktion auf.


Der erste Weg ist der saubere Weg. Insbesondere mit einem argc/argv 
Mechanismus wie ihn main() auch verwendet, ist das eine saubere Sache. 
Hat man nicht zuviele verschiedene Argumentlisten spricht auch nichts 
dagegen, einfach den 'größten gemeinsamen Nenner' aller Argumentlisten 
zu ermitteln und die Wrapperfunktionen damit aufzurufen.
Der zweite weg ist der dirty Weg, der in solchen Fällen meistens gar 
nicht so quick ist. Insbesondere wenn es viele verschiedene 
Argumentlisten, artet das in eine switch-case Schlacht aus. Und 
Pointer-Casten ist generell etwas, was immer einen anrüchigen 
Beigeschmack haben sollte.

von Uwe B. (boerge) Benutzerseite


Lesenswert?

Nobody schrieb:
> Dann mach wrapper-Funktionen mit va_.
>
wie müsste das ungefähr aussehen? (eine solche ungefähre Idee schwirrt 
auch in meinem Gehirn rum, aber der Ansatz fehlt bisher...)

... bzw. meinst du für jede Funktion eine eigene Wrapper-Funktion? 
Könnte man das noch irgendwie verallgemeinern, um nur eine 
Wrapper-Funktion zu haben?

Grüße & Danke Uwe

von Uwe B. (boerge) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> * Casten was das Zeug hält.
>   Zu deinen Funktionspointern kommt noch ein zusätzliches Attribut dazu,
>   das dir sagt, von welchem konkretem Typ der Funktionspointer ist.
>   (Du hast also einen Vorrat an verschiedenen Funktionspointer-
>   Datentypen und dieser Eintrag sagt dir welcher der richtige ist)
>   Dort siehst du nach, castest den Pointer zurecht und rufst die
>   Funktion auf.

... ungefähr kann ich mir dunkel vorstellen, was du meinst... hast du 
ein kleines Beispiel?

Grüße & Danke Uwe

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

> ... bzw. meinst du für jede Funktion eine eigene Wrapper-Funktion?

Kann man machen.
Man kann aber auch einfach nur für jede Funktionsklasse einen 
entsprechenden Wrapper bauen

> Könnte man das noch irgendwie verallgemeinern, um nur eine
> Wrapper-Funktion zu haben?

Auch das könnte man machen. Um casten kommt man nicht rum, solange man 
nicht für jede einzelne Funktion einen eigenen Wrapper schreibt.

von Oliver (Gast)


Lesenswert?

Falls du beim Aufruf einer Funktion nicht weißt, welche Funktion da 
tatsächlich aufgerufen wird, und du damit auch nicht weißt, welche 
Parameter diese benötigt, hast du ein Problem. Wenn es für die fehlenden 
Parameter sinnvolle default-Werte gibt, geht das über var args, oder 
eine standardisierte Maximal-Parameterliste. Wenn nicht, geht es gar 
nicht.

Wenn du das vor dem Aufruf schon weißt, was du aufrufst, reicht ein 
einfacher cast des Funktionspointers.

Oliver

von Nobody (Gast)


Lesenswert?

Uwe Berger schrieb:
> ... bzw. meinst du für jede Funktion eine eigene Wrapper-Funktion?
Ja.

> Könnte man das noch irgendwie verallgemeinern, um nur eine
> Wrapper-Funktion zu haben?

Sorry, aber es geht entweder va_ innerhalb der Funktion oder ausserhalb.

Die Wrapper-Funktion muss ja wissen wieviele Parameter sie auspacken 
soll.
Das geht aber nur aus der Parameterliste hervor. Für Metaprogramming 
dann mal LISP oder Smalltalk andenken.

Praktisch verallgemeinern kannst Du das mit einem Perl-Script der Dir zu 
einer gegebenen Deklaration einen Wrapper schreibt.

von Nobody (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
>> Könnte man das noch irgendwie verallgemeinern, um nur eine
>> Wrapper-Funktion zu haben?
>
> Auch das könnte man machen. Um casten kommt man nicht rum, solange man
> nicht für jede einzelne Funktion einen eigenen Wrapper schreibt.

Du meinst dann beim Aufruf des Wrappers?

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

>
> ... ungefähr kann ich mir dunkel vorstellen, was du meinst... hast du
> ein kleines Beispiel?

Keine Phantasie :-)
1
typedef void (*VoidFuncVoid)( void );
2
typedef void (*VoidFuncInt)( int );
3
typedef void (*VoidFuncLong)( long, long );
4
5
#define FUNC_VOID_VOID  0
6
#define FUNC_VOID_INT   1
7
#define FUNC_VOID_LONG  2
8
9
struct callFunc {
10
  void*   funcPtr;             // wir wissen nichts vom Funktionspointer
11
  uint8_t type;                // aber type sagt uns was das wirklich fuer einer ist
12
};
13
14
// die Funktionen themselfs
15
void func1( void )
16
{
17
}
18
19
void func2( int i )
20
{
21
}
22
23
void func3( long l, long k )
24
{
25
}
26
27
struct callFunc Functions[] = 
28
  { { func1, FUNC_VOID_VOID },
29
    { func2, FUNC_VOID_INT },
30
    { func3, FUNC_VOID_LONG },
31
  };
32
33
void callFunction( int Index, .... )
34
{
35
  if( Functions[ Index ].type == FUNC_VOID_VOID )
36
    *( (VoidFuncVoid)(Functions[ Index ].funcPtr)) ();
37
38
  else if( Functions[ Index ].type == FUNC_VOID_INT )
39
    *( (VoidFuncInt)(Functions[ Index ].funcPtr)) ( 5 );
40
     
41
  else if( Functions[ Index ].type == FUNC_VOID_LONG )
42
    *( (VoidFuncLong)(Functions[ Index ].funcPtr)) ( 8, 9 );
43
}

ungetesteter Code. Es kann sein, dass man den Compiler an der einen oder 
anderen Stelle mit noch einem Cast davon überzeugen muss, dass ich schon 
weiß was ich tue.
Die Sache mit der variablen Argumentliste und deren Einbau in den 
Wrapper darfst du machen.

Und überprüfe lieber doppelt und dreifach, ob die Einträge im Functions 
Array auch mit den tatsächlichen Gegebenheiten übereinstimmen. 
Zuwiderhandlungen werden mit Debugsitzungen nicht unter 3 Stunden 
bestraft.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich glaube mich zu erinnern (finde die Stelle aber gerade nicht im
Standard), dass eine Konvertierung eines beliebigen Funktionszeigers
auf einen anderen und wieder zurück durch den Standard abgedeckt ist.
Damit kann man sich einen "generischen Funktionszeigertypen" bauen,
mit dem der Zeiger selbst in einer Tabelle o. ä. untergebracht wird,
und der dann per typecast vor dem Aufruf in einen Funktionszeiger mit
der korrekten Parameterliste gewandelt wird (der Aufrufer sollte sich
natürlich sehr sicher sein, welche und wie viele Argumente zu über-
geben sind, aber sonst hat er sowieso ein Problem ;-).

Was jedoch nicht vom Standard abgedeckt ist, ist die Wandlung eines
Funktionszeigers in einen Objektzeiger (also z. B. "void *") und
zurück; es kann Architekturen geben, bei denen die Darstellung beider
Adressräume nicht verlustfrei ineinander überführt werden kann.  (Dafür
muss man gar nicht so weit laufen: bereits der AVR ist eine derartige
Architektur, lediglich der GCC handhabt sie nicht als solche, weil er
innerhalb einer Architektur-Portierung nur eine einzige Zeigergröße
kennt.)

p.s.: Genau das macht Karl Heinz aber gerade. ;-)

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:

> Was jedoch nicht vom Standard abgedeckt ist, ist die Wandlung eines
> Funktionszeigers in einen Objektzeiger (also z. B. "void *") und
> zurück; es kann Architekturen geben, bei denen die Darstellung beider
> Adressräume nicht verlustfrei ineinander überführt werden kann.

AH.
Ich wusste nicht mehr, ob man auf einen void* gehen darf oder nicht. Ist 
aber kein Beinbruch, dann nimmt man anstelle des void* einfach einen der 
Funktionspointer her und definiert den als den 'Standardfall' in den 
alle anderen gecastet werden um im Array Platz nehmen zu können und aus 
dem dann wieder herausgecastet wird.

Bei der bisher angerichteten Schweinerei kommts auf eine mehr oder 
weniger auch nicht mehr an :-)
Gegen die C-Version einer Smith&Wesson hat der Compiler keine Chance.

von Uwe B. (boerge) Benutzerseite


Lesenswert?

ok, danke für die Anregungen..., da habe ich erst mal eine ganze Menge 
Stoff zum Nachdenken und rumexperimentieren!

Falls ich nicht klar kommen sollte, kann ich mich ja nochmal melden.

Grüße Uwe

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl heinz Buchegger schrieb:
> Ist
> aber kein Beinbruch, dann nimmt man anstelle des void* einfach einen der
> Funktionspointer her und definiert den als den 'Standardfall'

Oder halt
1
typedef void (*generic_func_ptr)(void);

von Uwe B. (boerge) Benutzerseite


Lesenswert?

...ich nochmal zu dem Thema, nachdem ich über die gezeigten Ansätze 
geschlafen habe:

Ist es, um sich ein wildes Rumcasten zusparen, auch möglich, die 
Deklaration des Funktionspointers in der Tabelle mittels union zu 
überlagern? In der Wrapper-Funktion spricht man dann, so wie Karl Heinz 
es vorgeschlagen hat, an Hand des in der Tabelle mit abgelegten 
Zeiger-Typs die Variable mit dem entsprechenden Typ an...

Grüße & Danke Uwe

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Uwe Berger schrieb:
> Ist es, um sich ein wildes Rumcasten zusparen, auch möglich, die
> Deklaration des Funktionspointers in der Tabelle mittels union zu
> überlagern?

Ja, die Idee kam mir auch schon.

von Klaus W. (mfgkw)


Lesenswert?

Ist die union dann eleganter als wildes Rumcasten?

von Uwe B. (boerge) Benutzerseite


Lesenswert?

...naja, da könnte man sich drüber streiten. Impliziet läuft es auf das 
gleiche hinaus.

Aber ich bin der Meinung, dass es zumindestens transparender ist (und 
damit vielleicht einige Fehlerfallen weniger in sich verbirgt).

Grüße Uwe

von (prx) A. K. (prx)


Lesenswert?

Wenn der Compiler noch die alte per-ANSI-Syntax ohne Prototyping von 
Funktionen beherrscht, dann geht das recht einfach. Natürlich schaufelt 
man sich so das eigene Grab - aber wer darauf Lust hat, der möge es 
probieren.

Also
  void (*fptr)();
  void f1(a) { ... }
  void f2(a,b) double a; { ... }
und
  fptr = f2;
  fptr(3.14, 1);
  fptr = f1;
  fptr(0);

Wahrscheinlich muss man allerdings ein paar Warnungen des wohlmeinenden 
Compilers standhaft ignorieren.

von Klaus W. (mfgkw)


Lesenswert?

Das ist Leichenfledderei.
Leidest du unter Nekrophilie? :-)

von (prx) A. K. (prx)


Lesenswert?

Klaus Wachtler schrieb:

> Leidest du unter Nekrophilie? :-)

Wieso "leiden"? ;-)

Ohnehin hat die Fragestellung an sich schon einen Hang zu "gothic", da 
passt das doch prima rein.

von Klaus W. (mfgkw)


Lesenswert?

Dann viel Spaß!

Brrrr...

von Uwe B. (boerge) Benutzerseite


Lesenswert?

A. K. schrieb:
> Ohnehin hat die Fragestellung an sich schon einen Hang zu "gothic", da
> passt das doch prima rein.
>
:-) könnte stimmen, beim (privaten) Rumprogrammieren höre ich die 
entsprechend düstere Musik am liebsten.

Grüße Uwe

von Klaus W. (mfgkw)


Lesenswert?

Sowas höre ich nicht mal beim Rumtrinken.

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

> Aber ich bin der Meinung, dass es zumindestens transparender ist (und
> damit vielleicht einige Fehlerfallen weniger in sich verbirgt).

Nicht wirklich.

Die Fehleranfälligkeit ist ja nicht der Cast an sich. Die potentielle 
Falle steckt hier:
1
struct callFunc Functions[] = 
2
  { { func1, FUNC_VOID_VOID },
3
    { func2, FUNC_VOID_INT },
4
    { func3, FUNC_VOID_LONG },
5
  };

Du kannst hier wild Funktionspointer mit falschen Signaturkennungen 
versehen, ohne das dich irgendein Compiler der Welt davor schützen 
könnte Blödsinn zu machen. An dieser Stelle wird der 
Typprüfungsmechanismus ausgehebelt. Hier ist der Programmierer zu 100% 
in der Pflicht, das ohne Compilerunterstützung korrekt hinzukriegen und 
vor allen Dingen: auch in der Zukunft, wenn sich zb Funktionssignaturen 
verändern, hier auch nachzuziehen. Der Compiler wird dich nicht darauf 
aufmerksam machen, dass du hier ebenfalls nachbessern musst.

Alles Weitere sind dann nur noch syntaktische Varianten der Auswertung 
eben dieser Datenstruktur.

Aber dieses Array muss stimmen. Wenn es nicht korrekt aufgebaut wurde, 
dann krachts. Egal wie du es auswertest.

von Klaus W. (mfgkw)


Lesenswert?

Ja.

Das Problem ist, daß der TE unbedingt seine Funktionen nicht
ändern will. Für jede elegante und sauebre Lösung müsste er
aber genau das tun.
Es ist ein legitimer Wunsch, das nicht zu tun, aber dann muß
man zwangsläufig mit Murks leben und hat noch die Wahl, ein
möglichst kleines Übel zu nehmen.

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:


> Das Problem ist, daß der TE unbedingt seine Funktionen nicht
> ändern will. Für jede elegante und sauebre Lösung müsste er
> aber genau das tun.

Meine Erfahrung ist:
Solche vermeintlich schnell durchgeführte Krücken fallen einem immer 
irgendwann auf den Schädel.
Vielleicht nicht heute, vielleicht nicht morgen.
Aber irgendwann kommt der Boomerang.

Und dann ist in der Zwischenzeit alles noch viel schlimmer geworden. 
Diese Krücke hat noch andere Krücken hervorgebracht, wirkt sich u.U auf 
Dateiformate aus, Kunden haben mitlerweile Unmengen an Daten mit diesem 
Dateiformat, etc.

Kurz und gut: Ganz am Anfang der Geschichte hätte man alles noch einfach 
in den Griff kriegen können. Da ging es nur um ein paar Stunden Arbeit, 
die investiert werden müssten. Durch die Verschleppung ist man dann aber 
in der Situation, dass man die Krücke nicht einfach ausbauen und 
ersetzen kann. Zuviel anderes Zeugs hängt davon ab. Und dann sitzt man 
erst so richtig in der Sch...


> Es ist ein legitimer Wunsch, das nicht zu tun, aber dann muß
> man zwangsläufig mit Murks leben und hat noch die Wahl, ein
> möglichst kleines Übel zu nehmen.

Pest oder Cholera :-)

von Uwe B. (boerge) Benutzerseite


Lesenswert?

Nochmal Hallo zusammen...,

so ungefähr würde ich mir das mit union vorstellen (ohne den Wrapper, 
der je nach Typ und Anzahl der Parameter, die Parameter setzt und den 
entsprechenden Funktionspointer aufruft). Ist da jetzt ein eklatanter 
Fehler enthalten?
1
#include <stdio.h>
2
3
#define FUNC_VOID_VOID    0
4
#define FUNC_VOID_INT    1
5
#define FUNC_VOID_INT_INT  2
6
7
typedef struct {
8
      char* funct_name;
9
      union ftp {
10
        void (*VoidFuncVoid)(void);
11
        void (*VoidFuncInt)(int);
12
        void (*VoidFuncIntInt)(int, int);
13
      } funct_ptr;
14
      uint8_t type;
15
      uint8_t arg_count;
16
} callfunct_t;
17
18
19
void a(void) {
20
  printf("a");
21
}
22
23
void b(int p1) {
24
  printf("b: p1=%i", p1);
25
}
26
27
void c(int p1, int p2) {
28
  printf("c: p1=%i; p2=%i", p1, p2);
29
}
30
31
callfunct_t callfunct[] = {
32
    {"a", {a}, FUNC_VOID_VOID, 0},
33
    {"b", {b}, FUNC_VOID_INT, 1},
34
    {"c", {c}, FUNC_VOID_INT_INT, 2}
35
};
36
37
int main(void) {
38
  callfunct[0].funct_ptr.VoidFuncVoid();
39
  callfunct[1].funct_ptr.VoidFuncInt(1);
40
  callfunct[2].funct_ptr.VoidFuncIntInt(1, 2);
41
    return 0;
42
}

@Karl Heinz: mir ist klar, dass die Initialisierung der Tabelle 
callfunct[] kritisch ist und in meiner Verantwortung ist. In dem 
Zusammenhang verstehe ich auch die beiden Warnings, die der Compiler bei 
der Initialisierung der zweiten und dritten Tabellenzeile schmeisst:

 warning: initialization from incompatible pointer type

(Ich kann das Programm gerade nicht auf der Zielplattform (ein AVR, 
sonst hätte ich nicht hier gefragt ;-)...) ausprobieren, da ich sie 
nicht bei der Hand habe.)

Eine Frage noch: Warum warnt der Compiler bei der Initialisierung der 
Tabelle mit dem Spruch "warning: missing braces around initializer", 
wenn ich die Funktionsnamen nicht in {} schreibe? Steckt da noch 
Unverständnis meinerseits dahinter?

Grüße & Danke Uwe

PS.: achso, warum eigentlich diese ganze Geschichte? Ich habe ja vor 
einigen Tagen angefangen einen Basic-Interpreter zu 
schreiben/weiterzuentwickeln, der auf einem AVR läuft (siehe auch 
entsprechender Thread im Unterforum "Codesammlung"). Um diesen 
Interpreter im  Funktionsumfang drastisch zu erweitern möchte ich einen 
Basic-Befehl "call funct, p1, p2, ..." implementieren, der bestehende 
C-Routinen z.B. aus irgend einer Lib aufrufen kann.

von Klaus W. (mfgkw)


Lesenswert?

Uwe Berger schrieb:
> Eine Frage noch: Warum warnt der Compiler...

Weil du einen einfachen Wert (Zeiger) angibst, wo eigentlich ein
Aggregattyp (union) verlangt wird.

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

> Eine Frage noch: Warum warnt der Compiler bei der Initialisierung der
> Tabelle mit dem Spruch "warning: missing braces around initializer",
> wenn ich die Funktionsnamen nicht in {} schreibe? Steckt da noch
> Unverständnis meinerseits dahinter?

Weil innerhalb der struct noch eine union steckt.
Du brauchst für jedes Aggregat (struct oder union) eine {}

> Interpreter im  Funktionsumfang drastisch zu erweitern möchte ich einen
> Basic-Befehl "call funct, p1, p2, ..." implementieren, der bestehende
> C-Routinen z.B. aus irgend einer Lib aufrufen kann.

Speziell dann würde ich mir auf jeden Fall etwas völlig anderes 
überlegen. Da ist Tür und Tor für Fehler offen.

Merke: Wann immer du eine Benutzerinteraktion hast, und für einen 
BASCIC-Compiler ist nun mal das zu compilierende Programm seine 
'Benutzerinteraktion', kannst du nie genügend vorsichtig sein. Vertrauen 
dass der Benutzer schon das richtige eingeben wird, ist naiv (verzeih 
den Ausdruck)

von Karl H. (kbuchegg)


Lesenswert?

Uwe Berger schrieb:

> entsprechenden Funktionspointer aufruft). Ist da jetzt ein eklatanter
> Fehler enthalten?

Auf die Schnelle hab ich nichts gesehen.
Ich seh aber auch nicht, wie die union da irgendetwas vereinfachen 
würde. Denn das hier

> int main(void) {
>   callfunct[0].funct_ptr.VoidFuncVoid();
>   callfunct[1].funct_ptr.VoidFuncInt(1);
>   callfunct[2].funct_ptr.VoidFuncIntInt(1, 2);

Ist ja nicht die reale Anwendung. In der realen Anwendung weißt du ja 
nicht, dass an callfunct[0] ein VoidFuncVoid Pointer steckt.

von (prx) A. K. (prx)


Lesenswert?

In C99 kann man angeben, welches Element einer union initialisiert wird:

callfunct_t callfunct[] = {
    {"a", .funct_ptr.VoidFuncVoid = a, FUNC_VOID_VOID, 0},
    {"b", .funct_ptr.VoidFuncInt = b, FUNC_VOID_INT, 1},
    {"c", .funct_ptr.VoidFuncIntInt = c, FUNC_VOID_INT_INT, 2}
};

von Murkser (Gast)


Lesenswert?

Das mit den unions ist ja schon ganz nett, aber wie sieht es mit 
folgender Idee aus?

Statt Funktionen mit verschiedenen Parametern (und einer verschiedenen 
Anzahl Parameter) kann man die Funktionen allesamt so umschreiben, dass 
sie nur einen einzigen Parameter bekommen - eben eine Union aller 
moeglichen Parameter der verschiedenen Funktionen. Jede Funktion holt 
sich dann aus der Union ihre jeweiligen Argumente heraus, diese sind ja 
fuer jede Funktion bekannt.

Damit sind die Signaturen aller Funktionen gleich (je ein Parameter - 
die o.g. union) und Aufrufe ueber function pointer sind harmlos. Dies 
scheint mir im Vergleich der einfachste Ansatz zu sein.

  Murkser

von Klaus W. (mfgkw)


Lesenswert?

Naja, das ist eine von mehreren sauberen Möglichkeiten.
Der TE möchte aber seine Funktionen nicht ändern - was
zwar m.E. fragwürdig ist, aber des Menschen Wille sei
sein Himmelreich.

von Oliver (Gast)


Lesenswert?

Ob jetzt
1
 callfunct[2].funct_ptr.VoidFuncIntInt(1, 2);
oder
1
((VoidFuncIntInt)callfunct[2])(1, 2);
weniger "gothic" ist, ist wohl eher Geschmackssache.

Oliver

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> Naja, das ist eine von mehreren sauberen Möglichkeiten.
> Der TE möchte aber seine Funktionen nicht ändern - was
> zwar m.E. fragwürdig ist, aber des Menschen Wille sei
> sein Himmelreich.

Yepp.
Das mindeste was ich tun würde:
Vor die eigentlichen und vorhandenen Funktionen einen Wrapper setzen.

Der Wrapper kriegt die vereinheitlichte Argumentliste (wie auch immer 
die aussieht, union finde ich nicht so gut (*)), holt sich die für die 
Funktion relevanten Argumente dort heraus, macht meinetwegen noch ein 
paar Prüfungen darauf und ruft dann die eigentliche Funktion auf.

Die vorhandene Funktion muss nicht geändert werden, lediglich der 
Wrapper ist zu schreiben. Da es aber nicht unendlich viele 
unterschiedliche Argumentlisten geben wird, kann man sich da ein paar 
Hilfen bauen, so dass in weiterer Folge so ein Wrapper eine Frage von 
ein paar Minuten wird.

(*) Da das ganze offenbar sowieso in eine Runtime-virtuelle CPU 
eingebaut wird, ist das sogar noch viel einfacher: Die virtuelle CPU 
legt die Argumente auf den Callstack, schreibt noch die Anzahl an 
Argumenten auf den Stack und die Wrapperfunktionen holen sich das dann 
von dort wieder, Prüfen die Anzahl, holen die Argumente etc.


Das ganze läuft also darauf hinaus, der virtuellen CPU beizubringen mit 
variablen Argumentlisten umzugehen.

von Klaus W. (mfgkw)


Lesenswert?

> Vor die eigentlichen und vorhandenen Funktionen einen Wrapper setzen.

Dann kann man aber mit dem gleichen Aufwand auch gleich die Funktionen 
sinnvoll ändern. Aber jeder wie er will...

Wie sagte mein Bruder mal so schön: "Meine Katze mag die Mäuse roh,
ich mag sie nicht mal gekocht."

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
>> Vor die eigentlichen und vorhandenen Funktionen einen Wrapper setzen.
>
> Dann kann man aber mit dem gleichen Aufwand auch gleich die Funktionen
> sinnvoll ändern. Aber jeder wie er will...

Ich meinte nur:
Dann braucht er die eigentlichen Funktionen in den anderen Libraries 
nicht ändern. Das mag unter Umständen auch gar nicht so einfach sein, 
weil es noch andere Projekte gibt, die die Funktionen genau so haben 
wollen.

von Klaus W. (mfgkw)


Lesenswert?

Jo, das mag sein.
Ich habe aber eher den stillen Verdacht, daß die eh alle von ihm
sind und er nur keine Lust hat...


Wenn es um inneren Schweinehund geht, weiß ich, wovon ich rede!

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:

> Wenn es um inneren Schweinehund geht, weiß ich, wovon ich rede!

<verlegen mit der Fusspitze im Sand scharren>
Schnell weg :-)

von Uwe B. (boerge) Benutzerseite


Lesenswert?

Klaus Wachtler schrieb:
> Ich habe aber eher den stillen Verdacht, daß die eh alle von ihm
> sind und er nur keine Lust hat...
>
nein, sind sie erst mal nicht. Wenn es so wäre, würde ich wahrscheinlich 
die Sache "richtiger" machen und stimme euren Bedenken zu!

Die Idee zu dem call-Basic-Befehl kam mir, als ich jemanden, der relativ 
aktiv im c't-Bot-Projekt (http://de.wikipedia.org/wiki/C%E2%80%99t-Bot) 
ist,  von dem AVR-Basic-Interpreter erzählt habe. Er meinte, dass ein 
solcher Interpreter eine interessante Sache wäre, um Fahrverhalten, 
Grundfunktionen, keine Ahnung was noch und die als gegebene C-Funktionen 
vorliegen, nachladbar auf SD-Card zu haben und bei Bedarf ausführen zu 
können.

Wenn man das etwas allgemeiner betrachtet, bietet ein solcher 
call-Befehl fast unendliche Möglichkeiten im Hinblick auf den 
Funktionsumfang und die Anpassbarkeit des Interpreters für den 
jeweiligen Anwendungsfall. Von der Sache wäre es dann möglich, irgend 
eine bestehende, fremde, tausendmal bewährte Lib einzubinden und die 
Funktionen dem call-Befehl bekannt zu machen.

Warum soll ich z.B. die Initialisierung und Ausgabe auf ein LCD für den 
Basic-Interpreter neu schreiben, wenn es dafür bewährte Libs gibt? Es 
ist doch viel einfacher eine dieser Libs (unmodifiziert) einzubinden, um 
später  im Basic-Programm schreiben zu können:
1
10 call lcd_init
2
20 call lcd_gotoxy, 1, 2
3
30 call lcd_write, "Hallo Welt"
4
40 end

Grüße Uwe

PS.: es ist ein echter Interpreter, kein Compiler der Zwischen- oder 
Pseudocode vorproduziert. Das Basic-Programm wird im "Klartetxt" auf den 
AVR geladen und dann schrittweise analysiert und entsprechend 
ausgeführt. So ist der Ansatz...

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.