mikrocontroller.net

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


Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Nobody (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann mach wrapper-Funktionen mit va_.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Nobody (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Nobody (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Uwe Berger schrieb:

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

Keine Phantasie :-)
typedef void (*VoidFuncVoid)( void );
typedef void (*VoidFuncInt)( int );
typedef void (*VoidFuncLong)( long, long );

#define FUNC_VOID_VOID  0
#define FUNC_VOID_INT   1
#define FUNC_VOID_LONG  2

struct callFunc {
  void*   funcPtr;             // wir wissen nichts vom Funktionspointer
  uint8_t type;                // aber type sagt uns was das wirklich fuer einer ist
};

// die Funktionen themselfs
void func1( void )
{
}

void func2( int i )
{
}

void func3( long l, long k )
{
}

struct callFunc Functions[] = 
  { { func1, FUNC_VOID_VOID },
    { func2, FUNC_VOID_INT },
    { func3, FUNC_VOID_LONG },
  };

void callFunction( int Index, .... )
{
  if( Functions[ Index ].type == FUNC_VOID_VOID )
    *( (VoidFuncVoid)(Functions[ Index ].funcPtr)) ();

  else if( Functions[ Index ].type == FUNC_VOID_INT )
    *( (VoidFuncInt)(Functions[ Index ].funcPtr)) ( 5 );
     
  else if( Functions[ Index ].type == FUNC_VOID_LONG )
    *( (VoidFuncLong)(Functions[ Index ].funcPtr)) ( 8, 9 );
}


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.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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. ;-)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
typedef void (*generic_func_ptr)(void);

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ist die union dann eleganter als wildes Rumcasten?

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ist Leichenfledderei.
Leidest du unter Nekrophilie? :-)

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann viel Spaß!

Brrrr...

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sowas höre ich nicht mal beim Rumtrinken.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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:
struct callFunc Functions[] = 
  { { func1, FUNC_VOID_VOID },
    { func2, FUNC_VOID_INT },
    { func3, FUNC_VOID_LONG },
  };

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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?
#include <stdio.h>

#define FUNC_VOID_VOID    0
#define FUNC_VOID_INT    1
#define FUNC_VOID_INT_INT  2

typedef struct {
      char* funct_name;
      union ftp {
        void (*VoidFuncVoid)(void);
        void (*VoidFuncInt)(int);
        void (*VoidFuncIntInt)(int, int);
      } funct_ptr;
      uint8_t type;
      uint8_t arg_count;
} callfunct_t;


void a(void) {
  printf("a");
}

void b(int p1) {
  printf("b: p1=%i", p1);
}

void c(int p1, int p2) {
  printf("c: p1=%i; p2=%i", p1, p2);
}

callfunct_t callfunct[] = {
    {"a", {a}, FUNC_VOID_VOID, 0},
    {"b", {b}, FUNC_VOID_INT, 1},
    {"c", {c}, FUNC_VOID_INT_INT, 2}
};

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

@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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht 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}
};

Autor: Murkser (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Oliver (Gast)
Datum:

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

Oliver

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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."

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus Wachtler schrieb:

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

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

Autor: Uwe Berger (boerge) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
10 call lcd_init
20 call lcd_gotoxy, 1, 2
30 call lcd_write, "Hallo Welt"
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...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.