Usando a biblioteca C ++ em código C

102

Eu tenho uma biblioteca C ++ que oferece várias classes para gerenciamento de dados. Eu tenho o código-fonte da biblioteca.

Quero estender a API C ++ para oferecer suporte a chamadas de função C para que a biblioteca possa ser usada com código C e código C ++ ao mesmo tempo.

Estou usando a cadeia de ferramentas GNU (gcc, glibc, etc), portanto, o suporte a linguagem e arquitetura não é um problema.

Há alguma razão pela qual isso não seja tecnicamente possível?

Há alguma pegadinha que eu preciso tomar cuidado?

Existem recursos, código de exemplo e / ou documentação disponível sobre isso?


Algumas outras coisas que descobri:

  1. Use o seguinte para envolver seus cabeçalhos C ++ que precisam ser usados ​​pelo código C.

#ifdef __cplusplus
extern "C" {  
#endif  
//  
// Code goes here ...  
//  
#ifdef __cplusplus  
} // extern "C"  
#endif
  1. Mantenha as interfaces C ++ "reais" em arquivos de cabeçalho separados que não estão incluídos em C. Pense no princípio PIMPL aqui. Usar #ifndef __cplusplus #errorcoisas ajuda aqui a detectar qualquer loucura.
  2. Cuidado com os identificadores C ++ como nomes no código C
  3. Enums variando em tamanho entre compiladores C e C ++. Provavelmente não é um problema se você estiver usando a cadeia de ferramentas GNU, mas ainda assim, tome cuidado.
  4. Para structs, siga a seguinte forma para que C não se confunda.

    typedef struct X { ... } X
  5. Em seguida, use ponteiros para passar objetos C ++, eles apenas precisam ser declarados em C como struct X, onde X é o objeto C ++.

Tudo isso é cortesia de um amigo que é um mago em C ++.

Misha M
fonte
5
Um pouco tarde, mas escrevi um pequeno tutorial sobre C wrapper para C ++: teddy.ch/c++_library_in_c
Teddy

Respostas:

69

Sim, isso é certamente possível. Você precisará escrever uma camada de interface em C ++ que declare funções com extern "C":

extern "C" int foo(char *bar)
{
    return realFoo(std::string(bar));
}

Então, você chamará foo()de seu módulo C, que passará a chamada para a realFoo()função que é implementada em C ++.

Se você precisar expor uma classe C ++ completa com membros de dados e métodos, talvez precise fazer mais trabalho do que este exemplo de função simples.

Greg Hewgill
fonte
Deve extern "C"ser colocado apenas em declarações (e não em definições)? Porque você mencionou "a camada que declara funções", mas seu código de amostra também é uma definição. Em outras palavras, devemos colocá-lo em arquivos de cabeçalho ou arquivos de origem? (Ou ambos?)
kyriakosSt
@KyrSt: Se você tiver um arquivo de cabeçalho com uma declaração de função, você deve pelo menos colocar extern "C"lá. Seu compilador dirá se você também deve colocá-lo na definição.
Greg Hewgill,
23

C ++ FAQ Lite: "Como misturar códigos C e C ++" .

Algumas pegadinhas são descritas em respostas a estas perguntas:

  • [32.8] Como posso passar um objeto de uma classe C ++ para / de uma função C?
  • [32.9] Minha função C pode acessar dados diretamente em um objeto de uma classe C ++?
Alex B
fonte
12

Principal pegadinha: as exceções não podem ser capturadas em C. Se houver a possibilidade de uma exceção surgir no código C ++, escreva seu código C ou seus wrappers C ++ com muito cuidado. Por outro lado, mecanismos semelhantes a exceções (isto é, salto longo) no código C (como encontrados em várias linguagens de script) não são necessários para invocar destruidores para objetos C ++ na pilha.

ejgottl
fonte
2
Excelente ponto sobre chamadas de salto longo. Embora eu não os use diretamente, as estruturas de teste que uso os implementam. Algo para se manter em mente. Obrigado
Misha M
3

você pode misturar código C / C ++. Se sua função main () estiver em C ++, então você só precisa ter certeza de que suas funções c estão declaradas

extern "C"

Se o seu principal for C, provavelmente você está OK, exceto para as variáveis ​​estáticas. Quaisquer construtores com suas variáveis ​​estáticas devem ser chamados antes do início de main (). Isso não acontecerá se C for seu principal. Se você tiver muitas variáveis ​​estáticas, a melhor coisa a fazer é substituir as variáveis ​​estáticas por singletons.

David Nehme
fonte