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