Was muss man tun, damit der C++ Compiler eine C Funktion in eine obj Datei public macht?
-
Ich will eine umfangreiche C++ Library in Delphi benutzen. Dazu habe ich C-Wrapper Funktionen geschrieben die ich über OBJ-Dateien in Delphi einbinden kann.
Leider mussste ich feststellen, dass der C++ Compiler meine Wrapper-Funktionen nicht als PUBDEF in die obj-Datei ablegt. Das Problem läßt sich mit einer kleinen C/C++ Datei nachvollziehen:Wenn ich folgende C-Datei kompiliere, wird foo korrekt in die obj-Datei abgelegt:
#ifdef __cplusplus extern "C" { #endif int foo(void) { return 42; } #ifdef __cplusplus } #endif
Benenne ich die C-Datei zur CPP-Datei um. Dann klappt es nicht mehr. Mit TDump nachgeschaut, ist zu sehen, dass foo immer noch in der OBJ-Datei vorhanden ist, aber nicht als PUBDEF sondern als COMDEF (?). Der Delphi Compiler findet das Sympol deshalb nicht mehr. Ich habe kein Plan warum das so ist. Überall steht doch, dass extern "C" dafür sorgt, dass die Funktion als C-Funktion exportiert wird.
cdatei.c
Turbo Dump Version 6.1.0.0 Copyright (c) 1988-2008 CodeGear Display of File cdatei.c.obj 000000 THEADR cdatei.c 00000D COMENT Purge: Yes, List: Yes, Class: 0 (000h) Translator: CodeGear C++ 6.13 000025 COMENT Purge: Yes, List: Yes, Class: 232 (0E8h) Source File 1: cdatei.c 03/05/2010 02:02:01 pm 000039 COMENT Purge: Yes, List: Yes, Class: 233 (0E9h) Dependency File: cdatei.c 03/05/2010 02:02:01 pm 00004C COMENT Purge: Yes, List: Yes, Class: 233 (0E9h) End of Dependency List 000052 LNAMES Name 1: '_TEXT' Name 2: 'CODE' Name 3: '' Name 4: '_DATA' Name 5: 'DATA' Name 6: 'DGROUP' Name 7: '_BSS' Name 8: 'BSS' Name 9: '$$BSYMS' Name 10: 'DEBSYM' Name 11: '$$BTYPES' Name 12: 'DEBTYP' Name 13: '$$BNAMES' Name 14: 'DEBNAM' Name 15: '$$BROWSE' Name 16: '$$BROWFILE' 0000C0 SEGDEF 1 : _TEXT DWORD PUBLIC USE32 Class 'CODE' Length: 000a 0000CA SEGDEF 2 : _DATA DWORD PUBLIC USE32 Class 'DATA' Length: 0000 0000D4 SEGDEF 3 : _BSS DWORD PUBLIC USE32 Class 'BSS' Length: 0000 0000DE GRPDEF Group: DGROUP Segment: _BSS Segment: _DATA 0000E7 SEGDEF 4 : $$BSYMS BYTE PUBLIC USE32 Class 'DEBSYM' Length: 0050 0000F1 SEGDEF 5 : $$BTYPES BYTE PUBLIC USE32 Class 'DEBTYP' Length: 001a 0000FB SEGDEF 6 : $$BNAMES BYTE PUBLIC USE32 Class 'DEBNAM' Length: 0004 000105 SEGDEF 7 : $$BROWSE BYTE PUBLIC USE32 Class 'DEBSYM' Length: 0000 00010F SEGDEF 8 : $$BROWFILE BYTE PUBLIC USE32 Class 'DEBSYM' Length: 0000 000119 PUBDEF '_foo' Segment: _TEXT:0000 000127 LEDATA Segment: (1) _TEXT Offset: 0000 Length: 000A 0000: 55 8B EC B8 2A 00 00 00 5D C3 U...*...]. 000138 LEDATA Segment: (4) $$BSYMS Offset: 0000 Length: 0050 0000: 02 00 00 00 33 00 05 02 00 00 00 00 00 00 00 00 ....3........... 0010: 00 00 00 00 0A 00 00 00 00 00 00 00 08 00 00 00 ................ 0020: 00 00 00 00 01 00 00 00 00 10 00 00 01 00 00 00 ................ 0030: 00 00 00 00 04 5F 66 6F 6F 02 00 06 00 11 00 01 ....._foo....... 0040: 00 03 00 00 18 0A 42 43 43 33 32 20 36 2E 31 33 ......BCC32 6.13 00018F FIXU32 FixUp: 020 Mode: Seg Loc: Pointer32 Frame: TARGET Target: SI[1]: _TEXT 000197 LEDATA Segment: (5) $$BTYPES Offset: 0000 Length: 001A 0000: 02 00 00 00 0E 00 08 00 74 00 00 00 00 00 00 00 ........t....... 0010: 01 10 00 00 04 00 01 02 00 00 .......... 0001B8 LEDATA Segment: (6) $$BNAMES Offset: 0000 Length: 0004 0000: 03 66 6F 6F .foo 0001C3 LINN32 Segment: _TEXT 5:00000000h 7:00000003h 8:00000008h 0:0000000Ah 0001E1 MODE32
cdatei.cpp
Turbo Dump Version 6.1.0.0 Copyright (c) 1988-2008 CodeGear Display of File cdatei.cpp.obj 000000 THEADR cdatei.cpp 00000F COMENT Purge: Yes, List: Yes, Class: 0 (000h) Translator: CodeGear C++ 6.13 000027 COMENT Purge: Yes, List: Yes, Class: 232 (0E8h) Source File 1: cdatei.cpp 03/05/2010 02:02:01 pm 00003D COMENT Purge: Yes, List: Yes, Class: 233 (0E9h) Dependency File: cdatei.cpp 03/05/2010 02:02:01 pm 000052 COMENT Purge: Yes, List: Yes, Class: 233 (0E9h) End of Dependency List 000058 LNAMES Name 1: '_TEXT' Name 2: 'CODE' Name 3: '' Name 4: '_DATA' Name 5: 'DATA' Name 6: 'DGROUP' Name 7: '_BSS' Name 8: 'BSS' Name 9: '$$BSYMS' Name 10: 'DEBSYM' Name 11: '$$BTYPES' Name 12: 'DEBTYP' Name 13: '$$BNAMES' Name 14: 'DEBNAM' Name 15: '$$BROWSE' Name 16: '$$BROWFILE' 0000C6 SEGDEF 1 : _TEXT DWORD PUBLIC USE32 Class 'CODE' Length: 0000 0000D0 SEGDEF 2 : _DATA DWORD PUBLIC USE32 Class 'DATA' Length: 0000 0000DA SEGDEF 3 : _BSS DWORD PUBLIC USE32 Class 'BSS' Length: 0000 0000E4 COMDEF Name: 1: '_foo' virtual(_TEXT) Length: 000a bytes 0000F0 GRPDEF Group: DGROUP Segment: _BSS Segment: _DATA 0000F9 SEGDEF 4 : $$BSYMS BYTE PUBLIC USE32 Class 'DEBSYM' Length: 004f 000103 SEGDEF 5 : $$BTYPES BYTE PUBLIC USE32 Class 'DEBTYP' Length: 001a 00010D SEGDEF 6 : $$BNAMES BYTE PUBLIC USE32 Class 'DEBNAM' Length: 0004 000117 SEGDEF 7 : $$BROWSE BYTE PUBLIC USE32 Class 'DEBSYM' Length: 0000 000121 SEGDEF 8 : $$BROWFILE BYTE PUBLIC USE32 Class 'DEBSYM' Length: 0000 00012B LEDATA Segment: (4) $$BSYMS Offset: 0000 Length: 004F 0000: 02 00 00 00 32 00 05 02 00 00 00 00 00 00 00 00 ....2........... 0010: 00 00 00 00 0A 00 00 00 00 00 00 00 08 00 00 00 ................ 0020: 00 00 00 00 01 40 00 00 00 10 00 00 01 00 00 00 .....@.......... 0030: 00 00 00 00 03 66 6F 6F 02 00 06 00 11 00 01 00 .....foo........ 0040: 03 01 00 18 0A 42 43 43 33 32 20 36 2E 31 33 .....BCC32 6.13 000181 FIXU32 FixUp: 020 Mode: Seg Loc: Pointer32 Frame: TARGET Target: VEI[1]: _foo 00018A LEDATA Segment: (5) $$BTYPES Offset: 0000 Length: 001A 0000: 02 00 00 00 0E 00 08 00 74 00 00 00 00 00 00 00 ........t....... 0010: 01 10 00 00 04 00 01 02 00 00 .......... 0001AB LEDATA Segment: (6) $$BNAMES Offset: 0000 Length: 0004 0000: 03 66 6F 6F .foo 0001B6 LEDATA Segment: (16385) '_foo' Offset: 0000 Length: 000A 0000: 55 8B EC B8 2A 00 00 00 5D C3 U...*...]. 0001C8 LINN32 Segment: '_foo' 5:00000000h 7:00000003h 8:00000008h 0:0000000Ah 0001E7 MODE32
Nachtrag: Ich benutze Delphi und C++Builder 2009 Update 1. Ein Versuch mit Visual C++ 2008 Express hat ergeben, dass da dieses Problem nicht auftritt. Das nutzt mir natürlich nichts, da ich in Delphi keine COFF-OBJ-Dateien einbinden kann. Vermute nun das bei VB 2009 irgend ein Optionsschalter umgelegt werden muss. Hoffe weiterhin auf hilfe...
-
BerndD schrieb:
Nachtrag: Ich benutze Delphi und C++Builder 2009 Update 1. Ein Versuch mit Visual C++ 2008 Express hat ergeben, dass da dieses Problem nicht auftritt. Das nutzt mir natürlich nichts, da ich in Delphi keine COFF-OBJ-Dateien einbinden kann. Vermute nun das bei VB 2009 irgend ein Optionsschalter umgelegt werden muss. Hoffe weiterhin auf hilfe...
Das nutzt uns aber dass uns da das offenbar ein Compilerproblem und kein C++-Problem ist.
Was mich grade noch ein wenig wundert: Wenn du Delphi und C++Builder 2009 benutzt, wieso soll es dann helfen, bei Visual Basic 2009 einen Optionsschalter umzulegen?
-
Dieser Thread wurde von Moderator/in pumuckl aus dem Forum C++ in das Forum Compiler- und IDE-Forum verschoben.
Im Zweifelsfall bitte auch folgende Hinweise beachten:
C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?Dieses Posting wurde automatisch erzeugt.
-
pumuckl schrieb:
BerndD schrieb:
Nachtrag: Ich benutze Delphi und C++Builder 2009 Update 1. Ein Versuch mit Visual C++ 2008 Express hat ergeben, dass da dieses Problem nicht auftritt. Das nutzt mir natürlich nichts, da ich in Delphi keine COFF-OBJ-Dateien einbinden kann. Vermute nun das bei VB 2009 irgend ein Optionsschalter umgelegt werden muss. Hoffe weiterhin auf hilfe...
Das nutzt uns aber dass uns da das offenbar ein Compilerproblem und kein C++-Problem ist.
Was mich grade noch ein wenig wundert: Wenn du Delphi und C++Builder 2009 benutzt, wieso soll es dann helfen, bei Visual Basic 2009 einen Optionsschalter umzulegen?Sorry "VB 2009" ist ein Tippfehler. Ich meinte "CB 2009" für C++Builder 2009 :(.
-
Unter http://rvelthuis.de/articles/articles-cobjs.html habe ich einen interessanten Artikel zum Thema gefunden. Der Autor schreibt:
There is another limitation to what kind of files you can use. You can only use object files that are compiled as C files, not C++ files. For some reason, the Delphi linker has problems with object files that contain C++. This means that your source files must have the extension ".c" and not ".cpp". But since you can't use C++ classes directly anyway, that is not a severe limitation.
Das ist genau mein Problem. Die Aussage im letztem Satz kann ich aber nicht nachvollziehen. Jedenfalls verstehe ich nicht, wie man mit einen C-Compiler Wrapper kompilieren kann, die doch die C++ Classen, die sie „wrappen“ sollen, benutzen müssen. Auch die Header-Dateien die man einbinden muss, sind doch in C++ und nicht in C. Mache ich da einen Denkfehler? Wie könnte es gehen?
Ich habe zwischenzeitlich ein kleines "Hacker" Tool geschrieben, dass mir die C++ Obj-Datei in eine C Obj-Datei umbaut. Für einfache Sachen, wie z.B. obiges foo-Beispiel funktioniert das auch. Aber im eigentlichen Anwendungsfall meldet der Delphi-Linker lapidar, dass es sich nicht um eine gültige Objektdatei handelt.
In den guten alten Zeiten, als es noch Borland gab, gab es ein „Tool Interface Standard“ in der das OMF-Format offen gelegt war. Leider berücksichtigen diese alten Veröffentlichungen (z.B. http://refspecs.freestandards.org/elf/TIS1.1.pdf) nicht den ominösen C0-COMDEF Eintrag. Kennt jemand neuere Quellen?
-
BerndD schrieb:
Das ist genau mein Problem. Die Aussage im letztem Satz kann ich aber nicht nachvollziehen. Jedenfalls verstehe ich nicht, wie man mit einen C-Compiler Wrapper kompilieren kann, die doch die C++ Classen, die sie „wrappen“ sollen, benutzen müssen. Auch die Header-Dateien die man einbinden muss, sind doch in C++ und nicht in C. Mache ich da einen Denkfehler? Wie könnte es gehen?
Der am einfachsten gangbare Weg dürfte sein, aus dem C++-Code eine DLL zu machen und diese von Delphi aus einzubinden. Der Delphi-Compiler hat, wie du ja feststellst, nur sehr rudimentäre Unterstützung für das Linken von OMF-Objektdateien; simple, einigermaßen abhängigkeitsfreie C-Bibliotheken wie PCRE oder ZLib kann man so einbinden, aber C++-Code (der unvermeidlich zahlreiche Abhängigkeiten in die C++-RTL enthält und außerdem in anderen OMF-Einträgen resultiert) wirst du wohl nicht unterbekommen, auch wenn du einen C-Wrapper bastelst. Wrapper hin oder her, letztlich muß der C++-Code in deine DCU hineingelinkt werden, und das dürfte in aller Regel nicht funktionieren.
Alternativ könntest du deine Delphi-Anwendung in eine C++Builder-Anwendung konvertieren - d.h. eine neue leere C++Builder-Anwendung erstllen und alle Delphi-Units hinzufügen -; wenn du sowohl Delphi als auch C++Builder installiert hast, solltest du danach mit gewohntem Komfort (etwa Code Completion) an den Delphi-Units arbeiten können - und zusätzlich kannst du C++-Code in die Anwendung linken (wie man diesen dann in Delphi nutzbar macht, habe ich hier umrissen).
Falls sich dieser Ansatz verbietet, so würde ich erwägen, deine Anwendung mit Laufzeit-Packages zu kompilieren und ein C++Builder-Package zu erstellen, das in Delphi definierte Wrapper-Basisklassen oder -Interfaces implementiert.
BerndD schrieb:
Ich habe zwischenzeitlich ein kleines "Hacker" Tool geschrieben, dass mir die C++ Obj-Datei in eine C Obj-Datei umbaut. Für einfache Sachen, wie z.B. obiges foo-Beispiel funktioniert das auch. Aber im eigentlichen Anwendungsfall meldet der Delphi-Linker lapidar, dass es sich nicht um eine gültige Objektdatei handelt.
In den guten alten Zeiten, als es noch Borland gab, gab es ein „Tool Interface Standard“ in der das OMF-Format offen gelegt war. Leider berücksichtigen diese alten Veröffentlichungen (z.B. http://refspecs.freestandards.org/elf/TIS1.1.pdf) nicht den ominösen C0-COMDEF Eintrag. Kennt jemand neuere Quellen?
Allen Respekt - aber so viel Aufwand hätte ich da gar nicht investiert
Dennoch, technische Dokumentation zum OMF-Format und anderen Implementierungsdetails von Borland C++ findest du im Borland Open Architecture Handbook. Es ist im Wesentlichen auf die aktuellen C++-Compiler von Embarcadero übertragbar.
-
Ich gebe zu, dass es ein wenig “freaky” ist, sich so gegen die Verwendung von DLLs zu sträuben. Zumal Objs am Ende keine Vorteile bringen – deren Verwendung unterliegt weitestgehend denselben Einschränkungen. Die Idee die dahinter steckt, ist weniger eine ordentliche Einbindung, als vielmehr das schnell Einhacken in attraktiv erscheinende C++ Code. Oftmals kann man den Aufwand und den Nutzen nicht so recht einschätzen, der mit der Verwendung des fremden Codes verbunden ist. Da wäre es schön wenn man im ersten Anlauf eine Brechstange ansetzen könnte. Das setzt natürlich voraus, dass man sich die Brechstange einmal schmiedet. In diesem Fall also ein paar kleine Tools, die den Routinekram erledigen. Mit Problemen aus Richtung Exception-Handling, RTI und Funktionen aus der Laufzeitbibliotek habe ich gerechnet, dass ich aber so schnöde am Delphi-Linker scheitere, macht mich bockig. Ständig wird man im CodeGear RAD Studio als Delphi Programmiere mit C++ Kram belästigt und wenn man dann mal sagt: „Ok, her mit den Shit!“, dann zeigt einen der Delphi Linker den bösen Finger. In solchen Momenten bin ich echt nicht gut auf CodeGear (oder wie heißen die heute?) zu sprechen.
Die Borland Dokumentation kannte ich bereits. Ist aus dem vorigen Jahrtausend, seid dem scheint sich einiges geändert zu haben. Die „Open Architecture“ Ideale sind vermutlich längst begraben.
audacia schrieb:
Alternativ könntest du deine Delphi-Anwendung in eine C++Builder-Anwendung konvertieren - d.h. eine neue leere C++Builder-Anwendung erstllen und alle Delphi-Units hinzufügen -; wenn du sowohl Delphi als auch C++Builder installiert hast, solltest du danach mit gewohntem Komfort (etwa Code Completion) an den Delphi-Units arbeiten können - und zusätzlich kannst du C++-Code in die Anwendung linken (wie man diesen dann in Delphi nutzbar macht, habe ich hier umrissen).
Das klingt sehr Interessant. Der Weg von Delphi weg scheint auf alle Fälle besser ausgebaut zu sein, als der zu Delphi hin. Was ich für CodeGear tragisch finde, weil die besten C++ Compiler nicht aus ihrem Haus kommen. Der mit Abstand beste „Pascal“ Compiler aber schon.
Ok, genug Luft abgelassen. Werde mal probieren, wie sich ein C++ Projekt mit Delphi Units anfühlt.
-
BerndD schrieb:
Der Weg von Delphi weg scheint auf alle Fälle besser ausgebaut zu sein, als der zu Delphi hin.
Notwendigerweise, wie ich hier ausgeführt habe.
BerndD schrieb:
Der mit Abstand beste „Pascal“ Compiler aber schon.
Ich muß zugeben, daß ich den Oxygene-Compiler (-> Delphi Prism) derzeit interessanter finde. Unterstützung für Compile-time-AOP kannst du bei Java und C# lange suchen
Leider ist er für die typischen Anwendungsfälle von Delphi - nativer Code und Interaktion mit demselbigen - als .NET-Compiler natürlich kaum zu gebrauchen.
BerndD schrieb:
Werde mal probieren, wie sich ein C++ Projekt mit Delphi Units anfühlt.
Worauf du wahrscheinlich verzichten mußt, sind die Together-basierten Refactorings (einfache Dinge wie "Rename", die vom Chill-Parser getrieben werden, funktionieren weiterhin) und möglicherweise etwas Komfort beim Debuggen. Mich stört es eigentlich nicht, aber wenn es zum Problem wird, würde ich raten, die Entwicklung in verschiedenen Packages zu betreiben und nur das Endprodukt als C++Builder-Anwendung zu kompilieren.