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
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.
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
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
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.
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
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.
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?
Uwe Berger schrieb:>> ... ungefähr kann ich mir dunkel vorstellen, was du meinst... hast du> ein kleines Beispiel?
Keine Phantasie :-)
1
typedefvoid(*VoidFuncVoid)(void);
2
typedefvoid(*VoidFuncInt)(int);
3
typedefvoid(*VoidFuncLong)(long,long);
4
5
#define FUNC_VOID_VOID 0
6
#define FUNC_VOID_INT 1
7
#define FUNC_VOID_LONG 2
8
9
structcallFunc{
10
void*funcPtr;// wir wissen nichts vom Funktionspointer
11
uint8_ttype;// aber type sagt uns was das wirklich fuer einer ist
12
};
13
14
// die Funktionen themselfs
15
voidfunc1(void)
16
{
17
}
18
19
voidfunc2(inti)
20
{
21
}
22
23
voidfunc3(longl,longk)
24
{
25
}
26
27
structcallFuncFunctions[]=
28
{{func1,FUNC_VOID_VOID},
29
{func2,FUNC_VOID_INT},
30
{func3,FUNC_VOID_LONG},
31
};
32
33
voidcallFunction(intIndex,....)
34
{
35
if(Functions[Index].type==FUNC_VOID_VOID)
36
*((VoidFuncVoid)(Functions[Index].funcPtr))();
37
38
elseif(Functions[Index].type==FUNC_VOID_INT)
39
*((VoidFuncInt)(Functions[Index].funcPtr))(5);
40
41
elseif(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.
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. ;-)
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.
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
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
...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
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.
...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
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.
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.
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
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
structcallFuncFunctions[]=
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.
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.
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 :-)
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.
Uwe Berger schrieb:> Eine Frage noch: Warum warnt der Compiler...
Weil du einen einfachen Wert (Zeiger) angibst, wo eigentlich ein
Aggregattyp (union) verlangt wird.
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)
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.
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}
};
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
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.
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.
> 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."
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.
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!
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...