2012-08-13 8 views
6

Wir haben eine große, alte C++ - Anwendung mit viel Legacy-Code und ein paar externe Bibliotheken in C geschrieben. Diese Bibliotheken werden sehr selten aktualisiert - nur wenn wir einen Fehler finden Der Hersteller liefert einen Patch. Dies geschah letzte Woche mit einer Bibliothek, und nach der Integration der neuen Version haben wir herausgefunden, dass unser Build mit dieser Fehlermeldung bricht, wenn wir die Bibliothek nicht lokal ändern (Umgang mit C-Bibliothek anonyme Struktur-Typen in C++

non-local function ‘static E* MyCls::myFct(<anonymous struct>*)’ uses anonymous type 
)

Dies ist aufgrund der Bibliothek eine Reihe von Handle-Typen wie folgt erklärt:

#define _Opaque struct {unsigned long x;} * 

typedef _Opaque Handle; 
typedef _Opaque Request; 

die wir Funktion Unterschriften in einigen Klassen auf unserer Seite verwenden:

class MyCls { 
public: 
    static void* myFct(Handle handle); 
    ... 
} 

Das führt zu dem obigen Fehler, da der Compiler keinen ordnungsgemäßen Namen-Mangled-Namen für die Funktion (en) erstellen kann, da die _Opaque-Struktur keinen Name hat.

Unsere aktuelle Abhilfe für diese ist die Bibliothek Header-Datei patchen, explizit die Struktur einen Namen zu geben:

//#define _Opaque struct {unsigned long x;} * //Replaced by typedef below! 
typedef struct __Opaque {unsigned long x;} * _Opaque; 

Das ist offensichtlich schlecht ist, weil wir die Bibliothek, wenn möglich, nicht berühren wollen. Eine weitere, noch schlechtere Option wäre, die Typen in alle Funktionssignaturen in void* umzuwandeln und sie auf ihre jeweiligen Typen zurückzuschreiben. Und es gibt die schlechteste Möglichkeit, jede betroffene Funktion in reinem C ...

Also, meine Frage ist: Gibt es eine bessere Option als das Patchen der Bibliothek? Gibt es eine einfache Lösung, die ich übersehe? Was wäre der beste Weg, dies zu lösen?

+0

Sehen Sie nicht, was das mit C zu tun hat. Ihr Problem ist Name Mangling der C++ Wrapper, nein? –

+2

Dies könnte ein hervorragendes Argument für das Upgrade auf C++ 11 sein, für das diese Einschränkung nicht gilt. – ecatmur

+1

@JensGustedt, richtig, aber ich habe es auch markiert C, weil die Bibliothek in C geschrieben ist und technisch ist es ein C/C++ Interoperabilitätsproblem. – l4mpi

Antwort

3

Sie dies mit einer minimalen Änderung der #define Linie erreichen kann, in 7.1.3 die Regel zu nutzen: 8, dass der typedef-name ersten deklariert durch die Erklärung dass Klassentyp (oder enum-Typ) verwendet werden soll, den Klassentyp (oder enum-Typ) für Verknüpfungszwecke nur zur Bezeichnung:

#define MAKE_DUMMY2(line) dummy_ ## line 
#define MAKE_DUMMY(line) MAKE_DUMMY2(line) 
#define _Opaque struct {unsigned long x;} MAKE_DUMMY(__LINE__), * 

Dies ergibt Handle und Request usw. minimale Verknüpfung.

+0

das funktioniert, aber würde immer noch eine Änderung in der Bibliothek erfordern. Kannst du deine Antwort auch etwas erklären - warum zwei MAKE_DUMMY-Definitionen anstelle von einem verwenden? – l4mpi

+1

@ l4mpi Die beiden Definitionen sind notwendig, um '__LINE__' zu erweitern. Der Präprozessor ist * seltsam *; siehe http://stackoverflow.com/questions/1597007/creating-c-macro-with-and-line-token-concatenation-with-positioning-macr – ecatmur

+0

Wir haben den Bibliothekshersteller gefragt, ob er dieses Update einfügen kann. Wenn sie ablehnen, werden wir sie lokal einsetzen ... bis jemand die Zeit und den Mut hat zu testen, ob wir unseren Compiler und die C++ - Version von gcc4.1/C++ 98 auf gcc4.7/C++ 11 sicher aktualisieren können. – l4mpi

1

Sie können Namen einführen, indem Sie neue Typen deklarieren, die einfach diese Elemente enthalten. Verwenden Sie diese Typen für Ihre Parameter.

namespace MON { 
struct t_handle { 
    Handle handle; 
}; 

class MyCls { 
public: 
    static void* myFct(t_handle handle); 
    ... 
}; 
} 
+2

Der ctors und der Konvertierungsoperator können nicht deklariert werden, da ihre Signatur einen anonymen Typ enthalten würde. – ecatmur

+0

@ecatmur danke, dass du das notiert hast (+1). Mein Vorschlag für Ctors und Conversion-Ops war nicht kompatibel mit den Compiler/Einstellungen, die das OP verwendet - entfernt. – justin

+0

Dies wäre wahrscheinlich der richtige Weg gewesen, aber ich zögere, dies zu implementieren, da es Änderungen in einem großen Teil der Codebasis erfordern würde. – l4mpi

0

Dies scheint zu funktionieren:

class MyCls { 
    public: 
    typedef _Opaque MHandle; 
    static void* myFct(MHandle handle) { 
     return 0; 
    } 
}; 
1

Wenn Sie bereit sind, Ihre Methoden auf der Schnittstelle zu ändern, können Sie tun etwas besser als void *:

struct CHandle { 
    void *p; 
    CHandle(void *p): p(p) { } 
}; 
struct CRequest { 
    void *p; 
    CRequest(void *p): p(p) { } 
}; 

static CHandle make(Handle handle) { return CHandle(handle); } 
static Handle get(CHandle handle) { return static_cast<Handle>(handle.p); } 
static CRequest make(Request request) { return CRequest(request); } 
static Request get(CRequest request) { return static_cast<Request>(request.p); } 

Hier CHandle und CRequest Verknüpfung haben und so können in Ihrer Methode Signaturen verwendet werden; die Überladungen von make und get haben interne Verknüpfungen und können so mit den anonymen Typen kommunizieren. Sie können dies in eine Kopfzeile einfügen, sogar die static Funktionen.

Sie müssen Ihren Code ändern, damit z.MyCls::myFct ruft in die Bibliothek auf, Sie umschließen Parameter mit get und geben Werte mit make zurück.

+0

Warum funktioniert in diesem Fall 'make'? Es benutzt die anonyme Struktur in seiner Signatur ... sollte das nicht zu den gleichen Mangling Problemen führen? – l4mpi

+1

@ l4mpi In diesem Fall ist es OK, weil 'make' statisch ist und keine externe Verbindung hat. – ecatmur