2013-07-01 3 views
10

Ich möchte eine Inline-Art der Angabe, welche Prototypen in C++ enthalten sein sollen. Zum Beispiel:Gibt es eine Inline-Methode zum Mischen von C- und C++ - Prototypen?

 
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
IS_CPP void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 

zur Zeit mache ich:

 
#ifdef __cplusplus 
extern "C" { 
#endif 

....C HEADERS.. 

#ifdef __cplusplus 
} 

....C++ HEADERS... 

#endif 

aber es ist sehr unbequem, weil Überlastungen der gleichen Funktion in verschiedenen Orten sind. Ich könnte nur zwei verschiedene Header-Dateien haben, aber das ist auch ein Schmerz. Daher suche ich nach einer Inline-Lösung, wie ich sie oben vorgeschlagen habe. Kennt jemand eine Möglichkeit, das zu tun?

Antwort

14

Es ist einfacher als Sie denken.

#ifdef __cplusplus 
#define IS_C(x) extern "C" x ; 
#define IS_CPP(x) x ; 
#else 
#define IS_C(x) x ; 
#define IS_CPP(x) 
#endif 

Mit dieser Art von Kopf:

IS_C (void ArrayList_insert(ArrayList *arrlst, void *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, char *data, int i)) 
IS_CPP (void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)) 
+0

Dies wird einen Fehler verursachen, wenn mit c kompiliert wird, weil c keine Funktion überladen unterstützt. – chacham15

+0

chacham15 - Sie haben Recht. Ich habe die Antwort korrigiert, da ich die Frage falsch gelesen habe. –

+0

Jetzt entspricht es @Carl Norums Antwort. – chacham15

11

Sicher, können Sie es durch die Verwendung eines funktionsähnliche Makro fast wie Ihr Beispiel tun:

#ifdef __cplusplus 
#define IS_CPP(x) x 
#else 
#define IS_CPP(x) 
#endif 

     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, char *data, int i)); 
IS_CPP(void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i)); 

Nun, wenn Sie den Header als C++ kompilieren, Sie alle drei bekommen, aber wenn Sie als kompilieren C, du bekommst nur den einen. Wenn Sie eine einzelne Bibliothek zwischen den beiden teilen möchten, müssen Sie der C-Funktion beim Kompilieren für C++ einige extern "C" Qualifier hinzufügen. @ MarkLakatas Antwort zeigt einen möglichen Weg.

+0

+1 aber, gibt es eine Möglichkeit, es zu tun, ohne die abschließenden schließen paren? (Ich bin nur ein bisschen exzentrisch, sry) – chacham15

+0

Ich glaube nicht, nein. –

+0

-1, fehlt hier die notwendige Sprachverknüpfung. – Potatoswatter

1

Sie können offensichtlich den Präprozessor missbrauchen zusammen zu hacken, was Sie fragen, aber warum es tun? Persönlich würde ich lieber mylst.insert(foop, 1); anstelle von ArrayList_insert(mylst, foop, 1); eingeben, wenn ich C++ benutze. Mit anderen Worten, ich sehe wenig Nutzen darin, den C-Stil zum Aufrufen der überladenen Funktionen zu verwenden, aber das Mischen der Stile der Funktionsaufrufe, die Sie als Codeautor erstellt haben, ist auch nicht gerade hübsch.

Ich würde eine ArrayList-Klasse, die die gleichen Mitglieder wie in der C-Struktur hat und wann immer Sie Ihre Klasse mit den C-Funktionen verbinden müssen, erstellen Sie eine flache Kopie, wenn möglich und übergeben Sie diese an die C-Funktion dann Kopieren Sie die Informationen aus dieser Struktur zurück in Ihre Klasse.

Eine Alternative wäre, die Struktur in eine C++ - Klasse zu verpacken und diese für die C-Schnittstellenfunktionen zu verwenden.

Andernfalls könnten Sie versuchen, die Klasse die C-Struktur erben, vorausgesetzt, das Struktur-Tag hat nicht den Namen ArrayList und der Typdef für die Struktur ist nicht sichtbar von der C++ - Schnittstelle. Dann können Sie den This-Zeiger hoffentlich direkt aus der Member-Funktion übergeben, als wäre es die tatsächliche C-Struktur. Ich bin mir nicht sicher, ob diese Methode in allen Fällen portabel ist, also würde ich die erste Idee nach Möglichkeit implementieren, auch wenn es Daten erfordert, die Daten hin und her zu kopieren.

Alle Ideen vermeiden Code-Duplikation, und die C++ - Schnittstelle sieht eher wie C++ - Code statt der schlechten Mischung von C-Funktionen und C++ - Funktion Überladung. Zusätzlich werden die Schnittstellen etwas getrennt gehalten.Keine zusätzlichen Header-Dateien sind erforderlich, entweder als die C-Funktionen können in einem extern „C“ Block wie üblich gewickelt werden:

#ifdef __cplusplus 
extern "C" { 
#endif 

struct array_list_tag { 
    ... 
}; 

/* C functions here */ 

#ifdef __cplusplus 
} /* extern "C" */ 

class ArrayList ... 
#else /* !__cplusplus */ 
typedef struct array_list_tag ArrayList; 
#endif 
+0

"Alle Ideen vermeiden Code-Duplizierung" Genau. Ein Beispiel für einen Anwendungsfall ist, wenn Sie eine Bibliothek in C++ schreiben, die stark in C integriert sein soll. Sie mögen selbst die überladenen Funktionen aufrufen, aber es muss einen Weg für das C geben, mit dem Sie das Notwendige aufrufen funktioniert auch.Wrapping Strukturen in Klassen oder umgekehrt erhöhen die Menge der Code-Duplizierung und fügen eine Inkonsistenz in der Codierung Stil über die Plattform – chacham15

+0

Also Sie sagen, dass C in der Lage sein sollte, jede überladene Funktion aufrufen? Dann anstelle von ArrayList_insert für C nur wie das OP angefordert, ist es jetzt ArrayList_insertBuffer, ArrayList_insertString, ArrayList_insertRawData, etc.? Nun, die C++ - Schnittstelle würde mindestens übereinstimmen ... Wie beim Einbetten von Strukturen in Klassen, haben Sie wahrscheinlich recht. Ich bin mir sicher, dass es im C++ - Standard etwas gibt, das eine Funktion mit C-Verknüpfung benötigt, um auf C++ - Klassendaten zugreifen zu können, solange es keine Vererbung, virtuelle Funktionen und alle anderen Vorbehalte gibt, die mit Mischklassen und C. –

3

Die übliche Vorgehensweise ist, schreibt sie einfach in dem naheliegendste Weg:

void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
#ifdef __cplusplus 
void ArrayList_insert(ArrayList *arrlst, char *data, int i); 
void ArrayList_insert(ArrayList *arrlst, Buffer *data, int i); 
#endif /* __cplusplus */ 

Wie @ chacham15 weist darauf hin, Sie brauchen, in einem projektweiten Header,

#ifdef __cplusplus 
#define EXTERN_C extern "C" 
#endif /* __cplusplus */ 

und Sie müssen die C-aufrufbare Funktion mit EXTERN_C dekorieren.

+0

es ist nicht so einfach, Sie vermissen die '#ifdef __cplusplus extern" c "# endif 'Deklaration für die c-Funktion, die IMO macht es wirklich kludgy aussehen. – chacham15

+0

@ Chacham15: wie Sie anderswo kommentieren, '#ifdef __cplusplus #define IS_C extern" C "#else #define IS_C# endif' oder ähnlich (ich würde einen Namen wie" EXTERN_C "vorschlagen) kann einmal getan werden, dann sieht Pete Ansatz sehr sauber ... viel schöner als jede Zeile in einem Makro zu verpacken, doppelte '))', entweder mit dem Semikolon innerhalb des Makros oder visuell fehlt in jeder Zeile .... –

1

Wenn Sie wirklich den Textbaustein loswerden wollen und Sie bereit sind, den Präprozessor zu benutzen, dann machen Sie einfach weiter und schreiben Sie das Muster auf. Das allgemeine Muster haben Sie sieht aus wie

extern "C" { 
    void C_accessible_declaration(); // this is all C sees 
} 

void Cxx_accessible_declaration_1(int); 
void Cxx_accessible_declaration_1(long); 

So könnte man einen Makro machen,

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     extern "C" { C_DECLS } \ 
     CXX_DECLS 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, CXX_DECLS) \ 
     C_DECLS 
#endif 

Dies funktioniert, weil eine normale Funktionsdeklaration kein Komma nicht eingeschlossen durch Klammern enthalten. Wenn Sie möchten, dass es mit Vorlagen arbeitet (mit kommagetrennten Vorlagenparametern), können Sie Variadic-Makros, die in C99, C++ 11 und verschiedenen Compilern vor diesen Standards unterstützt werden, als Erweiterung verwenden.

#ifdef __cplusplus 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     extern "C" { C_DECLS } \ 
     __VA_ARGS__ 
#else 
# define C_PORTABLE_FUNCTION_SET(C_DECLS, ...) \ 
     C_DECLS 
#endif 

Jetzt funktioniert dies so lange, wie die C-Erklärungen, keine nackten Kommas enthalten, das heißt, Sie sollten nicht mehr Objekte in der einer Erklärung erklären. Ich habe es C_PORTABLE_FUNCTION_SET genannt, um zu betonen, dass es hauptsächlich sicher für die Verwendung mit Funktionsdeklarationen ist, aber beachten Sie, dass Sie C-zugängliche Objekte auch innerhalb von extern C deklarieren müssen. Gemeinsame struct Definitionen sollten überhaupt nicht geschützt werden; Sie sind durch das C++ - POD-Konzept geschützt und enthalten keine Sprachverbindungen.

Verbrauch:

#ifdef __cplusplus 
template< typename T, typename U > 
class Buffer { // still use #ifdef for the general case 
    ... 
}; 
#endif 

C_PORTABLE_FUNCTION_SET (
     void ArrayList_insert(ArrayList *arrlst, void *data, int i); 
, /* C++ */ 
     void ArrayList_insert(ArrayList *arrlst, char *data, int i); 

     template< typename T, typename U > 
     void ArrayList_insert(ArrayList *arrlst, Buffer< T, U > &data, int i); 
) 

Ich glaube nicht, dass ich das selbst tun würde, aber es scheint sicher genug idiomatische zu werden.

Verwandte Themen