Appeler une fonction C à partir du code C ++

90

J'ai une fonction C que je voudrais appeler à partir de C ++. Je n'ai pas pu utiliser le extern "C" void foo()type d'approche " " car la fonction C n'a pas pu être compilée avec g ++. Mais il se compile bien en utilisant gcc. Des idées comment appeler la fonction à partir de C ++?

Dangila
la source
1
Pourriez-vous s'il vous plaît écrire un exemple de code et g++des messages d'erreur
Matthieu Rouget
7
Si vous le compilez avec un compilateur C ++, c'est C ++. Le code C n'a pas à être compilé avec un compilateur C ++. Ce sont des langues différentes. Votre code n'est pas valide C ++ et par conséquent ne compile pas avec un compilateur C ++.
xaxxon
3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autiste
2
Mon essai: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_
1
Cela devrait être laissé ouvert, d'autant plus qu'il a de bonnes réponses indiquant comment le compilateur C (plutôt que C ++) peut être utilisé pour le code C.
Chris Stratton

Réponses:

126

Compilez le code C comme ceci:

gcc -c -o somecode.o somecode.c

Ensuite, le code C ++ comme ceci:

g++ -c -o othercode.o othercode.cpp

Puis liez-les ensemble, avec l'éditeur de liens C ++:

g++ -o yourprogram somecode.o othercode.o

Vous devez également indiquer au compilateur C ++ qu'un en-tête C arrive lorsque vous incluez la déclaration pour la fonction C. Alors othercode.cppcommence par:

extern "C" {
#include "somecode.h"
}

somecode.h devrait contenir quelque chose comme:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(J'ai utilisé gcc dans cet exemple, mais le principe est le même pour n'importe quel compilateur. Construisez séparément en C et C ++, respectivement, puis liez-le ensemble.)

Prof. Falken
la source
7
@Arne Bons points. Certains schmear certains C ++ dans leur C en enveloppant le extern "C"dans l'en-tête avec #ifdef __cplusplus.
détendre
@Arne Voir ma réponse ci-dessous. détendez-vous Comme vous pouvez le voir, je suis l'un d'entre eux;)
gx_
1
Merci beaucoup ! Cela m'a été très utile :)
Hesham Eraqi
Je recevais l'erreur suivante: Erreur: # 337: la spécification de liaison est incompatible avec le précédent "foo" (déclaré à la ligne 1) Maintenant sa compilation est correcte. Quelqu'un peut-il expliquer?
FaizanHussainRabbani
@FaizanRabbani, non sans beaucoup plus de détails.
Prof. Falken
61

Permettez-moi de rassembler les éléments des autres réponses et commentaires, pour vous donner un exemple avec du code C et C ++ clairement séparés:

La partie C:

toto.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

toto.c

#include "foo.h"

void foo(void)
{
    /* ... */
}

Compilez-le avec gcc -c -o foo.o foo.c.

La partie C ++:

bar.cpp

extern "C" {
  #include "foo.h" //a C header, so wrap it in extern "C" 
}

void bar() {
  foo();
}

Compilez ceci avec g++ -c -o bar.o bar.cpp

Et puis liez le tout ensemble:

g++ -o myfoobar foo.o bar.o

Raison d'être: Le code C devrait être du code C brut, pas de #ifdefs pour "peut-être qu'un jour j'appellerai cela d'une autre langue". Si un programmeur C ++ appelle vos fonctions C, c'est leur problème de faire cela, pas le vôtre. Et si vous êtes le programmeur C ++, alors l'en-tête C n'est peut-être pas le vôtre et vous ne devriez pas le changer, donc la gestion des noms de fonctions démêlés (c'est-à-dire le extern "C") appartient à votre code C ++.

Vous pouvez, bien sûr, vous écrire un en-tête C ++ pratique qui ne fait rien sauf envelopper l'en-tête C dans une extern "C"déclaration.

Arne Mertz
la source
7
Semble légitime. +1 pour la justification
gx_
Enfin une explication tout à fait claire de cela. Merci beaucoup!
Daniel Soutar
16

Je suis d'accord avec la réponse du professeur Falken , mais après le commentaire d'Arne Mertz, je veux donner un exemple complet (la partie la plus importante est la#ifdef __cplusplus ):

somecode.h

#ifndef H_SOMECODE
#define H_SOMECODE

#ifdef __cplusplus
extern "C" {
#endif

void foo(void);

#ifdef __cplusplus
}
#endif

#endif /* H_SOMECODE */

somecode.c

#include "somecode.h"

void foo(void)
{
    /* ... */
}

othercode.hpp

#ifndef HPP_OTHERCODE
#define HPP_OTHERCODE

void bar();

#endif /* HPP_OTHERCODE */

othercode.cpp

#include "othercode.hpp"
#include "somecode.h"

void bar()
{
    foo(); // call C function
    // ...
}

Ensuite, vous suivez les instructions du professeur Falken pour compiler et lier.

Cela fonctionne car lors de la compilation avec gcc, la macro __cplusplusn'est pas définie, donc l'en-tête somecode.hinclus dans somecode.cest comme ceci après le prétraitement:

void foo(void);

et lors de la compilation avec g++, alors __cplusplus est défini, et donc l'en-tête inclus dans othercode.cppest maintenant comme ça:

extern "C" {

void foo(void);

}
gx_
la source
4
thb, je n'aime pas le #ifdef __cpluspluscode en C. Le code C est le niveau inférieur, et il ne devrait pas avoir à s'inquiéter s'il peut être appelé à partir du code C ++ un jour. Imo qui #ifdefn'a son utilisation que dans le code C ++ si vous souhaitez fournir un en-tête de liaison C pour une bibliothèque écrite en C ++, et non l'inverse.
Arne Mertz
2
@ Prof.Falken bien sûr, mais c'est une définition destinée à être capable de fournir une compatibilité "descendante" du code C ++, pas du code C.
Arne Mertz
1

Cette réponse est inspirée d'un cas où la justification d'Arne était correcte. Un fournisseur a écrit une bibliothèque qui supportait autrefois à la fois C et C ++; cependant, la dernière version ne supportait que C. Les directives restantes suivantes laissées dans le code étaient trompeuses:

#ifdef __cplusplus
extern "C" {
#endif

Cela m'a coûté plusieurs heures à essayer de compiler en C ++. Appeler simplement C à partir de C ++ était beaucoup plus facile.

La convention ifdef __cplusplus est en violation du principe de responsabilité unique. Un code utilisant cette convention essaie de faire deux choses à la fois:

  • (1) exécuter une fonction en C - et -
  • (2) exécuter la même fonction en C ++

C'est comme essayer d'écrire en anglais américain et britannique en même temps. Cela lance inutilement une clé #ifdef __thequeensenglish #elif __yankeeenglish #else un outil inutile qui rend le code plus difficile à lire #endif dans le code.

Pour le code simple et les petites bibliothèques, la convention ifdef __cplusplus peut fonctionner; cependant, pour les bibliothèques complexes, il est préférable de choisir l'une ou l'autre langue et de s'y tenir. La prise en charge de l'une des langues demandera moins de maintenance que la prise en charge des deux.

Ceci est un enregistrement des modifications que j'ai apportées au code d'Arne pour le faire compiler sur Ubuntu Linux.

toto.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

toto.c

#include "foo.h"
#include <stdio.h>

void foo(void)
{
     // modified to verify the code was called
     printf("This Hello World was called in C++ and written in C\n");
}

bar.cpp

extern "C" {
    #include "foo.h" //a C header, so wrap it in extern "C" 
}

int main() {
  foo();
  return(0);
}

Makefile

# -*- MakeFile -*-
# dont forget to use tabs, not spaces for indents
# to use simple copy this file in the same directory and type 'make'

myfoobar: bar.o foo.o
    g++ -o myfoobar foo.o bar.o 

bar.o: bar.cpp
    g++ -c -o bar.o bar.cpp

foo.o: foo.c
    gcc -c -o foo.o foo.c
Agriculteur
la source