ubuntuusers.de

C/C++ Wie deklariert man eine funktion, die einen Zeiger auf eine Funktion liefert?

Status: Gelöst | Ubuntu-Version: Ubuntu 12.04 (Precise Pangolin)
Antworten |

Dakuan

Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Erstmal vorne weg, unter C funktioniert das eigentlich schon einige Zeit. Das Problem kommt erst dadurch zustande, das ich den Quelltext des betreffenden Moduls einfach per #include und "extern C" in ein cpp Projekt einbinde und dieser dadurch anstatt mit gcc mit g++ übersetzt wird. Ich möchte erreichen dass es mit beiden Compilern geht.

Hier mal die betreffenden Fragmente das C Quelltextes:

typedef struct {
    const char * chunkID;
    int         (*func)( unsigned char *, unsigned long );
} FuncTable;

...

/* ---------------------------------------------------------------------------
**      Table of the available chunk handling functions
*/
FuncTable fntab[] = {
    {"IHDR", png_header },
    {"PLTE", png_dummy },
    {"IDAT", png_dummy },
    {"IEND", png_dummy },
    {"tEXt", png_text },
    {"zTXt", png_ztxt },
    {"iTXt", png_text },
    { NULL,  NULL }
};

...

/* ---------------------------------------------------------------------------
**      Select a function for a given chunk name
**
**      chunkid     Chunk Name (4 character code)
**      ftab        the function table to use
**
**      returns:    adress of the handling function or NULL
*/
void * func_selector( char * chunkid, FuncTable * ftab ){
    FuncTable * ftp = ftab;
    
    while ( ftp->chunkID != NULL ){
        if ( strncmp( chunkid, ftp->chunkID, 4 ) == 0 ){
            return ftp->func;   /* hit! <<<<< dies ist die angemeckerte Zeile 228 */
        }
        ftp++;
    }
    return NULL;    /* no function found! */
}
...

In dieser Fassung komme ich aber nicht über diese Fehlermeldung hinweg:

In file included from pdb.cpp:111:
png_com.c: In function »void* func_selector(char*, FuncTable*)«:
png_com.c:228: Fehler: ungültige Umwandlung von »int (*)(unsigned char*, long unsigned int)« in »void*«

Aufgerufen wird das dann so:

    if( (handler = func_selector( ChunkType, fntab )) != NULL ) {
        success = (*handler)( ChunkBuffer, ChunkLength );
    }

Gibt es da eine Lösung?

microft

Avatar von microft

Anmeldungsdatum:
6. August 2009

Beiträge: 454

Wohnort: Norddeutschland

Auf den ersten schnellen Blick würd ich mal sagen der Compiler hat recht. Entweder ist deine Funktion eine Void * func() oder eine int func().

Du kannst keine int in einen Pointer umwandeln.

cu

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Natürlich, laut §1 hat der Compiler immer Recht. Aber was muss ich da machen?

Du kannst keine int in einen Pointer umwandeln.

Auch das ist mir klar. Sowas ging wohl nur beim ur K&R-C.

Aber wie mache ich dem C++ Compiler klar, das diese Funktion einen Zeiger auf eine Funktion liefert, die ihrerseits ein int abliefert?

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Dakuan schrieb:

Aber wie mache ich dem C++ Compiler klar, das diese Funktion einen Zeiger auf eine Funktion liefert, die ihrerseits ein int abliefert?

Indem Du als Rückgabewert der Fabrik-Funktion nicht void * wählst, sondern den Funktionspointer als Typen 😉

(Man sollte da stets mit typedef arbeiten - das sieht ja jetzt schon grausam aus 😉 )

Generell ist ja die Vermischung von C und C++ eher eklig, aber Deine Datenstruktur ist irgend wie... hm... suboptimal. Wieso nimmst Du kein Mapping? Da hast Du eine deutlich bessere Laufzeit!

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Hier mal ein Beispiel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

// wir definieren uns ``func`` als Typen
typedef int (func)(int param);

// eine Funktion, die durch ihre Signatur auf den Typen passt
int foo(int bar) {
  return bar + 1;
}

// kleine Fabrik-Funktion; beachte den Rückgabetypen!
func *get_func() {
  return foo;
}

int main(int argc, char **argv) {
  func *f = get_func();
  std::cout << f(1) << std::endl;
}

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Generell ist ja die Vermischung von C und C++ eher eklig,

Ja, da bin ich auch nicht glücklich mit. Aber da ich bei C++ auch noch ein Anfänger bin, löse ich komplizierte Dinge lieber mit C, zumal dann, wenn der Code in mehreren Projekten verwendet werden soll. In der C Umgebung und mit Scripten funktioniert der Code ja, und es gibt auch keine Warnungen beim compilieren.

Wieso nimmst Du kein Mapping?

Weil ich keine Ahnung habe, was das ist (nie gehört).

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Dakuan schrieb:

Ja, da bin ich auch nicht glücklich mit. Aber da ich bei C++ auch noch ein Anfänger bin, löse ich komplizierte Dinge lieber mit C,

Naja, so wirst Du C++ dann nur nicht lernen 😉

zumal dann, wenn der Code in mehreren Projekten verwendet werden soll.

Das ist natürlich etwas anderes. Wobei das eben auch suboptimal ist - wie Du ja gerade selber siehst!

Weil ich keine Ahnung habe, was das ist (nie gehört).

http://de.wikipedia.org/wiki/Assoziatives_Datenfeld

In C++ gibt es die Typen std::map und unordered_map.

In C kannst Du auf die Glib zurückgreifen. Hier gibts ein ganz nettes Tutorial dazu. Und hier ein kleines Beispiel, was ich vor olims Zeiten einmal geschrieben habe.

Du solltest Dich wirklich einmal mit den grundlegenden Datentypen befassen; das gehört zum Handwerkszeug eines jeden Informatikers!

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

zumal dann, wenn der Code in mehreren Projekten verwendet werden soll.

Das ist natürlich etwas anderes. Wobei das eben auch suboptimal ist - wie Du ja gerade selber siehst!

Ja, aber ich habe auch wenig Lust etliche alte Programme neu zu schreiben.

Aber wenn ich meine Fuktionstabelle mit Deinen Vorschlägen vergleiche, ist das doch gar nicht so weit davon entfernt, wenn man mal von dem Umweg über die Hash Tabelle absieht.

Nachtrag: Mit dem Umweg über typedef gibt sich cpp tatsächlich zufrieden

typedef int (funcptr)( unsigned char *, unsigned long );
...
funcptr * func_selector( char * chunkid, FuncTable * ftab ){
...
}

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Dakuan schrieb:

Aber wenn ich meine Fuktionstabelle mit Deinen Vorschlägen vergleiche, ist das doch gar nicht so weit davon entfernt, wenn man mal von dem Umweg über die Hash Tabelle absieht.

Man wählt einen Datentypen abhängig von den zu erwartenden Operationen; und diese wiederum geben dann Aufschluss darüber, welche Laufzeiten bei einer Operation bei einem Datentypen auftreten. Damit wählt man die optimale Laufzeit und damit rückwirkend den Typen.

Das Suche eine bestimmten Eintrags in einer Listen artigen Struktur hat eine Laufzeit von O(n). Der Zugriff auf einen Schlüssel in einem Mapping (je nach Implementierung) O(1) (also konstant und pfeilschnell; so beim Hashing) oder O(log n) bei jeder Art von Bäumen (B+ usw.) Da Du also einen bestimmten Eintrag abhängig von einem Wert holen willst, ist ein Mapping einer Liste überlegen! Ergo wäre hier ein Mapping die erste Wahl.

Solche generellen Angaben über die Zeitkomplexität bezeichnet man auch als Landau-Symbole.

Natürlich kann man jetzt argumentieren, dass bei 8 Einträgen der tatsächliche Mehraufwand (Hasing, Baum durchlaufen usw) ggf. sogar mehr kostet, als das Durchlaufen der Liste (insbesondere, wenn zu einem Großteil auf die ersten Elemente zugegriffen wird), jedoch geht es hier eher um etwas Prinzipielles: Wer an so einer Stelle nicht richtig überlegt, der tut das an anderer auch nicht. Und an einer anderen kann das schnell zu einem gigantischen Minus an Laufzeit führen ❗

Zudem gilt wie immer "premature opitimzation is the root of all evil" - insofern sollte man die natürlich passende Datenstruktur wählen. Ist einer Steller wie Deiner tatsächlich etwas zeitkritisch, so kann man ggf. immer noch für diese Stelle optimieren. Im ersten Ansatz ist ein Mapping hier einfach die erwartete Wahl.

Ich stelle Dir hiermit mal eine Aufgabe: Generiere Dir eine Liste von Zahlen zwischen 1 und 100 Mio. Packe dann eine beliebige Zahl aus der Liste noch einmal hinein; man erhält also ein Duplikat. Mische diese Ausgangsmenge zufällig. Nun schreibe ein Programm, welches basierend auf dieser Liste das Duplikat herausfindet.

Mische nun erneut die Liste und wende das Programm mehrfach darauf an.

Ich wette, ich kann in CPython - welches nicht gerade für seine Geschwindigkeit berühmt ist - ein Programm schreiben, dass diese Aufgabe schneller erledigt als Dein C-Programm - vorausgesetzt Du arbeitest auch da mit Arrays o.ä.

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Dakuan schrieb:

Nachtrag: Mit dem Umweg über typedef gibt sich cpp tatsächlich zufrieden

Das ist eher eine Abkürzung 😉 Zudem musst Du kein typedef nutzen; du kannst den Rückgabewert auch so hinschreiben - sieht dann natürlich verwirrend aus 😈

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Zudem musst Du kein typedef nutzen; du kannst den Rückgabewert auch so hinschreiben - sieht dann natürlich verwirrend aus

Das war mir schon klar, und ich habe auch diverse Versionen ausprobiert, aber ohne die richtige Version zu treffen.

Natürlich kann man jetzt argumentieren, dass bei 8 Einträgen der tatsächliche Mehraufwand (Hasing, Baum durchlaufen usw) ggf. sogar mehr kostet, als das Durchlaufen der Liste...

Genau das ist hier der Fall. Die Anzahl der Chunks ist nicht groß und nur wenige können mehrfach auftreten, die könnte ich dann an den Anfang der Liste setzen. Viele benötigen keine besondere Behandlung und können einfach mit der dummy() Methode erledigt werden. Jedenfalls ist das alles deutlich schneller, als die ursprüngliche (Bash) Scriptlösung unter Verwendung der üblichen Hilfsprogramme.

das gehört zum Handwerkszeug eines jeden Informatikers!

Ich stelle Dir hiermit mal eine Aufgabe:

Ich glaube Du schätzt mich falsch ein. Ich bin kein gelernter (studierter) Informatiker. Ich bin eher sowas wie ein Heimwerker und Autodidakt, was mich aber nicht davon abhält, auch ungewöhnliche Probleme anzupacken, besonders wenn die vorhandenen Standardlösungen keine befriedigende Lösungen bieten (was aber auch immer vom Anwendungsfall abhängt).

Lysander

Avatar von Lysander

Anmeldungsdatum:
30. Juli 2008

Beiträge: 2669

Wohnort: Hamburg

Dakuan schrieb:

Ich glaube Du schätzt mich falsch ein. Ich bin kein gelernter (studierter) Informatiker. Ich bin eher sowas wie ein Heimwerker und Autodidakt, was mich aber nicht davon abhält, auch ungewöhnliche Probleme anzupacken, besonders wenn die vorhandenen Standardlösungen keine befriedigende Lösungen bieten (was aber auch immer vom Anwendungsfall abhängt).

Ja und? Du programmierst - ergo ist das ein relevantes Thema für Dich. Es spielt doch keine Rolle, welche Ausbildung jemand hatte; ob Du nun Dachdecker oder Amateur-Heimwerker bist - wenn Du eine Leiste durchsägen musst, brauchst Du ein geeignetes Wissen über Hilfsmittel, die das ermöglichen.

Die Laufzeitanalyse ist enorm wichtig, um effizienten Code zu schreiben. Und da bilden die gängigen drei elementaren Datenstrukturen (Listen, Mappings und der Mengen) das Fundament. Je nach Variation der Implementierung bzw. des Konzeptes (Index basierte Liste vs. verkettete Liste, Stacks, Queues usw) haben die typischen CRUD Operationen darauf unterschiedliche Laufzeiten. Das ist wichtig zu wissen, damit man den richtigen Typen für das konkrete Problem wählt. Das schöne dabei ist: Diese Analyse musst Du nicht selber machen - das haben lauter kluge Leute bereits für einen erledigt (in zig theoretischen Abhandlungen 😀 ). Das kann man einfach nachlesen. Bei C++ sogar in der Doku (Abschnitt Complexity) zu jeder Operation.

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Es spielt doch keine Rolle, welche Ausbildung jemand hatte;

Ich wollte mit der Bemerkung nur vermeiden, dass Du Dinge voraussetzt, die möglicherweise nicht zutreffen. Zu meiner Zeit war übrigens noch Fortran IV aktuell und war wohl auch nur als Ergänzungsfach gedacht.

Übrigens, das Thema Mapping taucht in meinem C++ Buch tatsächlich auf, allerdings erst in der zweiten Hälfte und bis dahin bin ich leider noch nicht vorgedrungen (man kann so ein Buch ja nicht einfach wie ein Roman in 3 Tagen durchlesen).

Dee Team-Icon

Avatar von Dee

Anmeldungsdatum:
9. Februar 2006

Beiträge: 20095

Wohnort: Schwabenländle

Ich könnte ja jetzt eine Diskussion lostreten, versuche es aber zu vermeiden und will nur kurz meine Meinung anbringen, damit Lysanders Meinung nicht alleine steht.

Ich sehe das ähnlich wie Lysander, aber lustigerweise mit einem anderen Ergebnis: "premature opitimzation is the root of all evil" heißt für mich: Nutze das, was Du kannst, kennst und womit Du am besten zurecht kommst. Und erst wenn Du merkst, dass es irgendwo nicht passt (sei es, weil die Software schlecht zu pflegen oder zu langsam ist), dann optimierst Du – und kämst dann vielleicht auch auf Maps.

Ich habe Maps und Dictionarys in meiner kurzen C++-Laufbahn im Übrigen noch nie eingesetzt. Und lebe noch! ☺ Daher hat Lysander zwar Recht, dass Du die Werkzeuge kennen solltest (also zumindest vom Namen her – und das wäre in Deinem Buch ja noch gekommen), aber Du musst sie nicht einsetzen, wenn Du keine Problem mit den bisherigen Lösungen hast.

Gruß Dee

Dakuan

(Themenstarter)
Avatar von Dakuan

Anmeldungsdatum:
2. November 2004

Beiträge: 6512

Wohnort: Hamburg

Nutze das, was Du kannst, kennst und womit Du am besten zurecht kommst.

Deswegen greife bei vielen neuen Problemen, wenn ich die Zusammenhänge erst noch herausfinden muss, immer wieder auf C zurück.

... aber Du musst sie nicht einsetzen, wenn Du keine Problem mit den bisherigen Lösungen hast.

Momentan habe ich damit tatsächlich keine Probleme, da die Anzahl der Chunks, die ich in einigen Bilddateien suche recht begrenzt ist und ich die Suche bei einem Treffen sofort abbrechen kann. Das ist allemal schneller als die üblichen Scriptlösungen. Und bei einigen zig-tausend Dateien mache ich mir über die Laufzeit schon Gedanken. Aber immer wenn ich etwas neues einbaue, steht erstmal nur die Funktion im Vordergrund.

Übrigens, hatte ich vor etwas mehr als 10 Jahren schonmal mit Hash Tabellen gearbeitet. Den Algorithmus dazu hatte ich seinerzeit aus dem Buch "Algorithmen und Datenstrukturen" von Niklaus Wirth abgeschrieben (und nach C portiert) und das hat damals gut funktioniert. Der Begriff "Mapping" taucht darin allerdings nicht auf (keine Ahnung ob es den Begriff damals schon gab oder wie da die Zusammenhänge sind).

Antworten |