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.


Log in to reply