[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