[linux-l] Function Pointer in C (was: java Klotz am Bein ?)
Axel Weiß
aweiss at informatik.hu-berlin.de
Do Mai 19 15:17:58 CEST 2005
Am Mittwoch, 18. Mai 2005 22:00 schrieb Rocco Rutte:
> >wie machst Das das in C eleganter?
>
> struct foo {
> char* key;
> int (*handle) (char* key, list_t* args, unsigned long data);
> unsigned long data;
> };
>
> Damit braucht man nur noch:
>
> struct foo Foo[] = {
> { "foo", handle1, 0 },
> { "bar", handle2, 0 },
> { NULL, NULL, 0 }
> };
>
> Und braucht quasi nur noch:
>
> int config_exec (list_t* l) {
> int i = 0;
> while (Foo[i].key) {
> if (safe_strcmp (Foo[i], l->data[0]) == 0 && Foo[i].handle)
> return (Foo[i].handle (l->data[0], l, Foo[i].data));
> i++;
> }
> return (0);
> }
>
> int config_parse (const char* line) {
> list_t* l = list_from_str (line, " \t");
> int rc = config_exec (l);
> list_del (&l, _free);
> return (rc);
> }
Hi Rocco,
das ist sehr elegant, geht aber auch in Java (s.u.).
> Vorurteilsfrei: das einzige, was ich in Java in dieser Richtung
> gesehen habe sind lange Orgien, wo mittels TokenizerIrgendwas ein
> String in String[] aufgespalten wurde und dann sowas wie:
>
> if (t.getFirst.equals ("foo"))
> // behandle keyword "foo"
> else if (t.getFirst.equals ("bar"))
> // behandle keyword "bar"
> ...
Nein, Du kannst im Prinzip das selbe machen, wie in C. Mit dem
Unterschied, dass Du alles in Klassen packen musst (Java ist
Objekt-orientiert, gell;).
> Der Vorteil an Callback ist IMHO, dass man nicht alles direkt in den
> Code frickeln muss, weil es dort nicht hingehört, und wenn es nur ein
> Aufruf pro Keyword ist. Man könnte ja auch auf die Idee kommen,
> solche Keywords von außen dynamisch hinzufügen zu wollen oder eine
> Liste mit allen Keywords haben zu wollen...
Callbacks sind in der OO-Welt abstrakte Methoden. Das Konzept dahinter
heißt 'spätes Binden' (late binding) und funktioniert im Prinzip
genauso wie Funktionspointer, nur dass die eben in der Sprache
gekapselt sind (bzw. an Objekte gebunden).
> Auch wenn man da wild mit Pointern durch die Gegend schiesst, was
> manchmal gefährlich sein kann, erreicht man dadurch sauberes
> Information Hiding auch locker in C und man spart es sich, sehr viel
> Zeug immer direkt in den Code zu schreiben sondern packt es
> ordentlich in Methoden. Für ein Projekt habe ich zum Beispiel:
>
> void* hash_new (int size);
> ...
> void hash_map (void*, int (*mapfunc) (const char*, void*, unsigned
> long), unsigned long);
Was in C (finde ich) immer sehr mühsam ist: Du musst alles explizit
machen. Aber genau diese Dinge (mit Pointern schießen) werden bei den
OO-Sprachen in Konzepte gesteckt, und Du kriegst die Pointer nicht mehr
zu sehen. Anders ausgedrückt: die Verwaltung von Callbacks geschieht in
den OO-Sprachen implizit.
> Es muss außer der Implementierung niemand wissen, wie ich die Tabelle
> organisiere bzw. ob es überhaupt eine Tabelle ist, ein Baum, was auch
> immer. Der Aufrufer bekommt bei der map-Funktion (ähnlich
> funktionalen Sprachen) nur den aktuellen Schlüssel, die dazugehörigen
> Daten und wahlweise extra Informationen. Da kann man sauber
> Funktionalität wie "nur bestimmte Schlüssel zurückgeben" aus der
> Tabelle raushalten.
Genau das kannst Du mit Vererbung aber auch erreichen.
> Auch wenn da schrecklich viele Klammern, Sternchen und & drin
> vorkommen werden, so habe ich als derjenige, der nur das schlanke
> Interface kennt, quasi alle Möglichkeiten über den Callback.
Perl ist viel schlimmer ;)
> Ich tendiere eher dazu, die generischsten EierLegendenWollmichSauen
> haben zu wollen, quasi supereinfachübersichtlich und trotzdem sehr
> mächtig. Das geht mit solchen Konstrukten IMHO ganz gut auch wenn die
> Gefahr besteht, dass man den Überblick verliert.
Dann brauchst Du die Super-Basis-Klasse ;)
> Ich will damit nicht sagen, dass ich Java per se nicht mag und C ja
> sowieso ganz toll ist, die Frage nach dem Wie war ernst gemeint.
Ich hab Dir mal ein Beispiel gemacht. Jetzt bin ich nicht soo der
Java-Experte (spreche lieber C++) und vielleicht ist das nicht gerade
astrein umgesetzt, aber die Konzepte sind in den beiden Sprachen
gleich, und um die geht es mir hier. (Wenn Du willst, bau ich das in
C++ nochmal.)
Was Du brauchst ist eine abstrakte Klasse für Callbacks, die heißt hier
mal 'handle'. Wichtig ist, eine abstrakte Methode zu haben, die von den
einerbenden Klassen implementiert wird (in C hast Du dafür nen
Funktionszeiger). Und weil Du auf Schlüsselworte reagieren willst,
verpassen wir der handle-Klasse gleich ihren Schlüssel.
Von handle erben mal zwei Klassen, 'handle_a' und 'handle_b', die auf
ihr Schlüsselwort "a" bzw: "b" lauschen und im Callback das Gewünschte
erledigen.
Dann legen wir ein handle-Array an und füllen es mit je einem Objekt der
Klassen handle_a und handle_b. Über dieses Array wird nachher iteriert,
die Schlüssel verglichen und der Callback gerufen.
Der Rest des Beispiels ist Testumgebung: ein Datentyp für String-Paare
und ein bischen Aktion im foo-Konstruktor. Die innere Schleife sieht
dann genauso aus wie bei Dir in C.
class foo{
abstract class handle{
public String key;
public handle(String _key){key = new String(_key);}
public abstract void f(String arg);
};
class handle_a extends handle{
public handle_a(){super("a");}
public void f(String arg){System.out.println("callback a:" + arg);}
};
class handle_b extends handle{
public handle_b(){super("b");}
public void f(String arg){System.out.println("b's callback:" + arg);}
};
handle[] bar = {new handle_a(), new handle_b()};
class pair{
public String key, value;
public pair(String _key, String _value){key=_key; value=_value;}
};
public foo(){
pair[] pair = {new pair("a", "dummy1"),
new pair("b", "dummy2")};
for (int i=0; i<pair.length; ++i){
for (int j=0; j<bar.length; ++j){
if (pair[i].key.equals(bar[j].key)){
bar[j].f(pair[i].value);
}
}
}
};
public static void main(String[] argv){
foo foo = new foo();
}
};
Ist auch elegant, oder?
Axel
--
Humboldt-Universität zu Berlin
Institut für Informatik
Signalverarbeitung und Mustererkennung
Dipl.-Inf. Axel Weiß
Rudower Chaussee 25
12489 Berlin-Adlershof
+49-30-2093-3050
** www.freesp.de **
Mehr Informationen über die Mailingliste linux-l