Chame uma função C a partir do código C ++

87

Tenho uma função C que gostaria de chamar de C ++. Não pude usar o extern "C" void foo()tipo de abordagem " " porque a função C falhou ao ser compilada usando g ++. Mas compila bem usando o gcc. Alguma idéia de como chamar a função de C ++?

Dangila
fonte
1
Você poderia escrever algum código de exemplo e g++mensagens de erro
Matthieu Rouget
7
Se você compilá-lo com um compilador C ++, é C ++. O código C não precisa ser compilado com um compilador C ++. São línguas diferentes. Seu código não é C ++ válido e, portanto, não é compilado com um compilador C ++.
xaxxon
3
@MatthieuRougetvoid valid_in_C_but_not_in_CPlusPlus(size_t size) { char variable_length_array[size]; }
autista
2
Minha tentativa: void f(void *pv) { int *pi = pv; *pi = 42; }^^
gx_
1
Isso deve ser deixado em aberto, especialmente porque tem boas respostas apontando como o compilador C (em vez de C ++) pode ser usado para o código C.
Chris Stratton

Respostas:

122

Compile o código C assim:

gcc -c -o somecode.o somecode.c

Então o código C ++ como este:

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

Em seguida, vincule-os, com o vinculador C ++:

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

Você também precisa informar ao compilador C ++ que um cabeçalho C está chegando ao incluir a declaração da função C. Então othercode.cppcomeça com:

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

somecode.h deve conter algo como:

 #ifndef SOMECODE_H_
 #define SOMECODE_H_

 void foo();

 #endif


(Eu usei gcc neste exemplo, mas o princípio é o mesmo para qualquer compilador. Construa separadamente como C e C ++, respectivamente, e então vincule-os.)

Prof. Falken
fonte
7
@Arne Bons pontos. Algumas pessoas schmear algum C ++ em seu C envolvendo o extern "C"no cabeçalho com #ifdef __cplusplus.
relaxe
@Arne Veja minha resposta abaixo. descontrair Como você pode ver, sou um deles;)
gx_
1
Muito obrigado ! Isso foi muito útil para mim :)
Hesham Eraqi
Estava recebendo o seguinte erro: Erro: # 337: a especificação de ligação é incompatível com o "foo" anterior (declarado na linha 1) Agora está compilando bem. Alguém pode explicar?
FaizanHussainRabbani
@FaizanRabbani, não sem muito mais detalhes.
Prof. Falken
61

Deixe-me reunir os fragmentos de outras respostas e comentários, para dar um exemplo com códigos C e C ++ separados de forma clara:

A parte C:

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.c

#include "foo.h"

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

Compile isso com gcc -c -o foo.o foo.c.

A parte C ++:

bar.cpp

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

void bar() {
  foo();
}

Compile isso com g++ -c -o bar.o bar.cpp

E então conecte tudo junto:

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

Justificativa: O código C deve ser um código C simples, sem #ifdef"talvez algum dia eu chame isso de outra linguagem". Se algum programador C ++ chama suas funções C, é o seu problema de como fazer isso, não o seu. E se você é o programador C ++, então o cabeçalho C pode não ser seu e você não deve alterá-lo, então o tratamento de nomes de função não fragmentados (ou seja, oextern "C" ) pertence ao seu código C ++.

Você pode, é claro, escrever um cabeçalho C ++ conveniente que não faz nada, exceto envolver o cabeçalho C em um extern "C" declaração.

Arne Mertz
fonte
7
Parece legítimo. +1 para a justificativa
gx_
Finalmente uma explicação completamente clara disso. Muito obrigado!
Daniel Soutar
16

Concordo com a resposta do Prof. Falken , mas após o comentário de Arne Mertz quero dar um exemplo completo (a parte mais importante é o #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
    // ...
}

Então você segue as instruções do Prof. Falken para compilar e vincular.

Isso funciona porque, ao compilar com gcc, a macro __cplusplusnão está definida, então o cabeçalho somecode.hincluído somecode.cfica assim após o pré-processamento:

void foo(void);

e ao compilar com g++, then __cplusplus é definido e, portanto, o cabeçalho incluído othercode.cppagora é assim:

extern "C" {

void foo(void);

}
gx_
fonte
4
thb, eu não gosto do #ifdef __cpluspluscódigo C. O código C é o nível inferior e não deve se preocupar se algum dia poderá ser chamado a partir do código C ++. Imo que #ifdeftem seu uso apenas em código C ++ se você deseja fornecer um cabeçalho de ligação C para uma biblioteca escrita em C ++, e não o contrário.
Arne Mertz
2
@ Prof.Falken é claro, mas é uma definição destinada a ser capaz de fornecer compatibilidade "descendente" de código C ++, não para código C.
Arne Mertz
1

Essa resposta é inspirada por um caso em que o raciocínio de Arne estava correto. Um fornecedor escreveu uma biblioteca que antes suportava C e C ++; no entanto, a versão mais recente suportava apenas C. As seguintes diretivas vestigiais deixadas no código eram enganosas:

#ifdef __cplusplus
extern "C" {
#endif

Isso me custou várias horas tentando compilar em C ++. Simplesmente chamar C de C ++ foi muito mais fácil.

A convenção ifdef __cplusplus viola o princípio de responsabilidade única. Um código que usa essa convenção tenta fazer duas coisas ao mesmo tempo:

  • (1) executar uma função em C - e -
  • (2) executar a mesma função em C ++

É como tentar escrever em inglês americano e britânico ao mesmo tempo. Isso está jogando desnecessariamente uma #ifdef __thequeensenglish spanner #elif __yankeeenglish wrench #else uma ferramenta inútil que torna o código mais difícil de ler #endif no código.

Para código simples e pequenas bibliotecas, a convenção ifdef __cplusplus pode funcionar; entretanto, para bibliotecas complexas, é melhor escolher um idioma ou outro e ficar com ele. O suporte a um dos idiomas exigirá menos manutenção do que tentar oferecer o suporte a ambos.

Este é um registro das modificações que fiz no código de Arne para compilá-lo no Ubuntu Linux.

foo.h :

#ifndef FOO_H
#define FOO_H

void foo(void);

#endif 

foo.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
Agricultor
fonte