Estou trabalhando em um projeto que possui muito código C legado . Começamos a escrever em C ++, com a intenção de, eventualmente, converter também o código legado. Estou um pouco confuso sobre como o C e C ++ interagem. Entendo que envolver o código C com extern "C"
o compilador C ++ não alterará os nomes do código C , mas não tenho muita certeza de como implementá-lo.
Portanto, na parte superior de cada arquivo de cabeçalho C (após os protetores de inclusão), temos
#ifdef __cplusplus
extern "C" {
#endif
e na parte inferior, escrevemos
#ifdef __cplusplus
}
#endif
Entre os dois, temos todos os nossos incluem, typedefs e protótipos de função. Tenho algumas perguntas para verificar se estou entendendo isso corretamente:
Se eu tiver um arquivo C ++ A.hh que inclua um arquivo de cabeçalho C Bh, inclua outro arquivo de cabeçalho C Ch, como isso funciona? Eu acho que quando o compilador entrar em Bh,
__cplusplus
será definido, então ele envolverá o código comextern "C"
(e__cplusplus
não será definido dentro deste bloco). Portanto, quando entrar em Ch,__cplusplus
não será definido e o código não será envolvidoextern "C"
. Isso está correto?Há algo de errado em envolver um pedaço de código
extern "C" { extern "C" { .. } }
? O que o segundoextern "C"
fará?Não colocamos esse wrapper nos arquivos .c, apenas nos arquivos .h. Então, o que acontece se uma função não tiver um protótipo? O compilador acha que é uma função C ++?
Também estamos usando algum código de terceiros que está escrito em C e não possui esse tipo de wrapper. Sempre que incluo um cabeçalho dessa biblioteca, venho colocando uma questão em
extern "C"
torno da #include. É este o caminho certo para lidar com isso?Finalmente, isso é uma boa ideia? Há mais alguma coisa que devemos fazer? Vamos misturar C e C ++ no futuro próximo, e quero ter certeza de que estamos cobrindo todas as nossas bases.
fonte
To ensure that the names declared in that portion of code have C linkage, and thus C++ name mangling is not performed.
(Eu tenho que partir do link )Respostas:
extern "C"
realmente não muda a maneira como o compilador lê o código. Se o seu código estiver em um arquivo .c, ele será compilado como C, se estiver em um arquivo .cpp, será compilado como C ++ (a menos que você faça algo estranho à sua configuração).O que
extern "C"
faz é afetar a ligação. As funções C ++, quando compiladas, têm seus nomes mutilados - é isso que torna a sobrecarga possível. O nome da função é modificado com base nos tipos e no número de parâmetros, para que duas funções com o mesmo nome tenham nomes de símbolos diferentes.O código dentro de um
extern "C"
ainda é código C ++. Existem limitações sobre o que você pode fazer em um bloco externo "C", mas tudo sobre ligação. Você não pode definir nenhum novo símbolo que não possa ser construído com o vínculo C. Isso significa que não há classes ou modelos, por exemplo.extern "C"
blocos ninho bem. Também existeextern "C++"
se você se encontrar irremediavelmente preso dentro deextern "C"
regiões, mas não é uma boa idéia do ponto de vista da limpeza.Agora, especificamente sobre suas perguntas numeradas:
Em relação ao item 1: __cplusplus permanecerá definido dentro dos
extern "C"
blocos. Porém, isso não importa, já que os blocos devem se encaixar perfeitamente.Em relação a # 2: __cplusplus será definido para qualquer unidade de compilação que esteja sendo executada no compilador C ++. Geralmente, isso significa arquivos .cpp e todos os arquivos incluídos nesse arquivo .cpp. O mesmo .h (ou .hh ou .hpp ou o que você tem) pode ser interpretado como C ou C ++ em momentos diferentes, se diferentes unidades de compilação os incluírem. Se você deseja que os protótipos no arquivo .h se refiram aos nomes dos símbolos C, eles devem ter
extern "C"
ao serem interpretados como C ++, e não devem terextern "C"
ao serem interpretados como C - daí a#ifdef __cplusplus
verificação.Para responder à sua pergunta nº 3: funções sem protótipos terão ligação C ++ se estiverem em arquivos .cpp e não dentro de um
extern "C"
bloco. Isso é bom, no entanto, porque, se não tiver um protótipo, só poderá ser chamado por outras funções no mesmo arquivo e, em geral, você não se importará com a aparência da ligação, porque não planeja ter essa função. seja chamado por qualquer coisa fora da mesma unidade de compilação.Para o nº 4, você conseguiu exatamente. Se você estiver incluindo um cabeçalho para o código que possui ligação C (como o código que foi compilado por um compilador C), será necessário
extern "C"
o cabeçalho - para que você possa vincular à biblioteca. (Caso contrário, seu vinculador procuraria funções com nomes como_Z1hic
quando você estava procurandovoid h(int, char)
5: Esse tipo de mixagem é um motivo comum de se usar
extern "C"
, e não vejo nada de errado em fazer dessa maneira - apenas certifique-se de entender o que está fazendo.fonte
extern "C++"
quando seu cabeçalho C ++ / code está preso profundamente dentro de algum código C__cplusplus
é determinar seC++
está sendo usado vsC
, então defini-lo manualmente / desafia explicitamente o objetivo dele ...extern "C"
não altera a presença ou ausência da__cplusplus
macro. Ele apenas altera a ligação e a identificação de nomes das declarações agrupadas.Você pode aninhar
extern "C"
blocos muito feliz.Se você compilar seus
.c
arquivos como C ++, qualquer coisa que não esteja em umextern "C"
bloco e sem umextern "C"
protótipo será tratada como uma função C ++. Se você os compilar como C, é claro que tudo será uma função C.sim
Você pode misturar com segurança C e C ++ dessa maneira.
fonte
.c
arquivos como C ++, tudo será compilado como código C ++, mesmo que esteja em umextern "C"
bloco. Oextern "C"
código não pode usar recursos que dependem de convenções de chamada C ++ (por exemplo, sobrecarga do operador), mas o corpo da função ainda é compilado como C ++, com tudo o que isso implica.Algumas dicas que são colunistas da excelente resposta de Andrew Shelansky e que discordam um pouco não mudam realmente a maneira como o compilador lê o código
Como os protótipos de suas funções são compilados como C, não é possível sobrecarregar os mesmos nomes de funções com parâmetros diferentes - esse é um dos principais recursos do nome desconfigurado do compilador. É descrito como um problema de ligação, mas isso não é verdade - você receberá erros do compilador e do vinculador.
Os erros do compilador ocorrerão se você tentar usar os recursos C ++ da declaração de protótipo, como sobrecarga.
Os erros do vinculador ocorrerão mais tarde porque sua função parecerá não ser encontrada, se você não tiver o wrapper externo "C" em torno das declarações e o cabeçalho estiver incluído em uma mistura de fontes C e C ++.
Um motivo para desencorajar as pessoas a usar a configuração de compilação C como C ++ é porque isso significa que o código-fonte não é mais portátil. Essa configuração é uma configuração de projeto e, se um arquivo .c for solto em outro projeto, ele não será compilado como c ++. Prefiro que as pessoas renomeiem os sufixos de arquivo para .cpp.
fonte
É sobre a ABI, para permitir que aplicativos C e C ++ usem interfaces C sem nenhum problema.
Como a linguagem C é muito fácil, a geração de código ficou estável por muitos anos para diferentes compiladores, como GCC, Borland C \ C ++, MSVC etc.
Enquanto o C ++ se torna cada vez mais popular, muitas coisas devem ser adicionadas ao novo domínio C ++ (por exemplo, finalmente, o Cfront foi abandonado na AT&T porque o C não podia cobrir todos os recursos necessários). Como o recurso de modelo e a geração de código em tempo de compilação, no passado, os diferentes fornecedores do compilador realmente fizeram a implementação real do compilador e vinculador C ++ separadamente, as ABIs reais não são de todo compatíveis com o programa C ++ em plataformas diferentes.
As pessoas ainda podem gostar de implementar o programa real em C ++, mas ainda mantêm a interface C e a ABI antigas, como de costume, o arquivo de cabeçalho deve declarar extern "C" {} , informa ao compilador gerar C ABI compatível / antiga / simples / fácil para as funções de interface se o compilador for compilador C e não compilador C ++.
fonte