Por que C fornece 'ligações' de linguagem onde C ++ fica aquém?

60

Recentemente, eu queria saber quando usar C sobre C ++ e vice-versa? Felizmente, alguém já me venceu e, apesar de demorar um pouco, fui capaz de digerir todas as respostas e comentários para essa pergunta.

No entanto, um item nessa postagem continua sendo abordado repetidamente, sem nenhum tipo de exemplo, verificação ou explicação:

"O código C é bom para quando você deseja ter várias ligações de idioma para sua biblioteca"

Isso é uma paráfrase. Devo observar que várias pessoas apontam que várias ligações de linguagem são possíveis em C ++ (através de algum externfuncionamento), mas, no entanto, se você ler essa publicação na íntegra, é bastante óbvio que C é ideal para portabilidade / ligação de linguagem. Minha pergunta é: por que?

Alguém pode fornecer razões concretas pelas quais escrever bibliotecas em C facilita ligações e / ou portabilidade em outros idiomas?

smeeb
fonte
6
Principalmente por razões históricas e sociais. A maioria das implementações de linguagens e sistemas de tempo de execução são construídos acima C. Por exemplo, os tempos de execução Ocaml, sbcl, Haskell são codificados em C (e não vai ganhar muito sendo recodificados em C ++)
Basile Starynkevitch
8
Na prática, colar bibliotecas C ++ genuínas (pense em Qt) nesses tempos de execução é doloroso.
Basile Starynkevitch
3
@ Basile: Qt não é uma biblioteca C ++ genuína. Além disso, mesmo para bibliotecas mais dolorosas, é apenas doloroso porque elas expressam semânticas realmente úteis.
DeadMG 4/15
2
Leia COM essencial por Don Box. Explica o processo de criação de uma ABI em C ++. COM foi a maneira da Microsoft de criar uma ABI. ou seja, as bibliotecas COM C ++ podem ser usadas em C ou VB ou em outros idiomas.
User93353 5/05

Respostas:

69

C possui uma interface muito, muito mais simples, e suas regras para converter uma interface de código-fonte em uma interface binária são simples o suficiente para que a geração de interfaces externas à qual se ligar seja feita de maneira bem estabelecida. O C ++, por outro lado, tem uma interface incrivelmente complicada e as regras para a ligação ABI não são padronizadas, nem formalmente nem na prática. Isso significa que praticamente qualquer compilador de qualquer idioma para qualquer plataforma pode se vincular a uma interface C externa e saber exatamente o que esperar, mas para uma interface C ++, é essencialmente impossível porque as regras mudam dependendo de qual compilador, qual versão e qual plataforma com a qual o código C ++ foi construído.

Em C, também não há regras padrão de implementação de linguagem binária, mas é uma ordem de magnitude mais simples e, na prática, os compiladores usam as mesmas regras. Outro motivo para dificultar a depuração do código C ++ é a gramática complicada mencionada acima, pois os depuradores freqüentemente não conseguem lidar com muitos recursos de linguagem (coloque pontos de interrupção nos modelos, comandos de conversão de ponteiro de análise nas janelas de exibição de dados etc.).

A falta de uma ABI (interface binária de aplicativo) padrão tem outra conseqüência - torna impraticável o envio de interfaces C ++ para outras equipes / clientes, pois o código do usuário não funcionará, a menos que seja compilado com as mesmas ferramentas e opções de compilação. Já vimos outra fonte desse problema - a instabilidade das interfaces binárias devido à falta de encapsulamento do tempo de compilação.

- C ++ com defeito

Mason Wheeler
fonte
4
Você deve observar que, embora seja possível definir uma API do tipo C implementada em C ++ ( extern "C", ainda é mais trabalho adicional fazê-lo, que deve ser equilibrado com os recursos fornecidos pelo C ++)
jhominal
5
A ABI do C ++ é irrelevante no contexto da questão, na qual as bibliotecas C ++ podem ser facilmente exportadas extern "C".
DeadMG 4/15/15
12
@ jhominal: É muito melhor do que escrevê-lo em C, onde você precisa definir uma interface C e também implementá-lo em C, enquanto que em C ++ você pode definir uma interface C e não precisa implementar em C. Não importa em que idioma você esteja implementando, você ainda precisará definir uma interface C - isso é verdade em C ++, pois em C ou em qualquer outra linguagem que possa expor as ligações em C.
DeadMG 4/15
9
Seria possível adicionar um aviso de isenção de responsabilidade ao link para essa bobagem do FQA?
Martin Ba
2
@ DocBrown: Claro, parece-me que a pergunta é sobre ligações em linguagem C vs. ligações em linguagem C ++.
Mason Wheeler
32

Se você está tentando se comunicar com um falante de outro idioma, o pidgin é mais fácil do que o inglês shakespeariano.

Os conceitos de C - chamadas de função, ponteiros, seqüências terminadas em NULL - são muito diretos, portanto outros idiomas podem implementá-los facilmente o suficiente para chamar funções em C. Por razões históricas, muitos outros idiomas são implementados em C, o que torna as funções de chamada C ainda mais fáceis.

O C ++ adiciona várias classes de coisas, com herança e vtables e modificadores de acesso; exceções, com pilha desenrolando e alterando o fluxo de controle; modelos. Tudo isso torna mais difícil para outros idiomas usar ligações C ++: na melhor das hipóteses, há mais "cola" ou código de interoperabilidade para implementar e, na pior, os conceitos não são traduzidos diretamente (devido a diferenças nos modelos de classe, manipulação de exceção, etc.) Para modelos em particular, o simples uso (instanciação) deles normalmente requer uma etapa de compilação com um compilador C ++, o que complica bastante o uso de outros ambientes.

Com tudo isso dito, é possível exagerar a dificuldade de fornecer ligações de uma biblioteca C ++ para outro idioma:

  • As ligações C ++ podem ser tão compatíveis quanto C, se você estiver disposto a trabalhar nisso. Como o @DeadMG salienta, o C ++ suporta extern "C", então você pode exportar ligações de linguagem no estilo C (com toda a simplicidade e compatibilidade das ligações C) de uma biblioteca C ++ (com a limitação de que você não pode expor nenhuma funcionalidade específica do C ++) .
  • Outra objeção comum às ligações da linguagem C ++ é a falta de estabilidade da ABI para C ++, mas isso também é exagerado; As ABIs C ++ são menos padronizadas que as ABIs C, mas existem padrões e padrões de fato (o Itanium C ++ ABI, que também é usado no OS X ; o padrão de fato do GCC para Linux). O Windows é pior, mas mesmo no Windows, permanecer em uma versão do Visual C ++ deve funcionar bem.
Josh Kelley
fonte
11
O outro problema ao fornecer uma ligação de uma biblioteca C ++ para outro idioma é que o outro idioma pode exigir que as ligações sejam implementadas em C. Ou, para idiomas que possuem algo como os tipos (.NET) P / Invoke ou (python), pode não fornecer nenhuma ferramenta para usar o C ++ ABI.
Random832
6
@ Random832: O que é completamente irrelevante quando o lado C ++ pode simplesmente oferecer a interface C. Você não precisa implementar a ligação em C para oferecer uma ligação C.
DeadMG 4/15/15
21

C é um dos idiomas mais antigos ainda existentes. Sua ABI é simples e praticamente todos os sistemas operacionais ainda em uso atualmente foram escritos nela . Enquanto alguns desses sistemas operacionais podem ter adicionado coisas, por exemplo, em C # /. NET ou qualquer outro item no topo, lá embaixo, eles estão muito mergulhados em C.

Isso significa que, para usar a funcionalidade fornecida pelo sistema operacional, praticamente todas as linguagens de programação existentes precisam de uma maneira de interagir com as bibliotecas C de qualquer maneira . Perl, Java, C ++, todos eles fornecem formas nativas de "falar C", porque precisavam se não quisessem reinventar todas as rodas existentes.

Isso faz do C o latim das linguagens de programação. (Quantos anos de internet antes dessa metáfora deve ser "o inglês das línguas de programação"?)


Ao escrever sua biblioteca em C, você obtém uma interface compatível com C de graça (obviamente). Se você estiver escrevendo sua biblioteca em C ++, poderá obter ligações C, por meio de extern "C"declarações mencionadas.

No entanto , você pode obter essas ligações única para a funcionalidade que pode ser expresso em C .

Portanto, a API da sua biblioteca não pode usar ...

  • modelos,
  • aulas,
  • exceções,
  • quaisquer funções que tomem ou retornem objetos.

Um exemplo simples, você precisaria para fazer suas funções exportadas tomar e retornar matrizes ( []) em vez de std::vector(ou std::stringpara que o assunto).

Portanto, você não apenas não seria capaz de fornecer as coisas boas que o C ++ tem a oferecer aos clientes da sua biblioteca, mas também teria que fazer um esforço adicional para "traduzir" a API da sua biblioteca de C ++ para "compatível com C" ( extern "C").

Por isso, pode- se afirmar que C é a melhor escolha para implementar uma biblioteca. Pessoalmente, acho que os benefícios do C ++ ainda superam o esforço necessário para uma extern "C"API, mas sou apenas eu.

DevSolar
fonte
O Windows parece estar tentando se basear no .NET, o Android se baseia no Java (também C como um detalhe de implementação para algumas APIs) e o iOS / OSX no Objective-C.
User253751
11
O inglês já é a língua dominante no mundo da programação. Mais dominante do que em outras profissões.
Siyuan Ren
3
@immibis: Windows, Linux / Android e BSD / OSX são todos os kernels escritos em C, com interfaces escritas em (e para) C. Não importa o que é construído além disso, o Java precisa de JNI, o Perl precisa de chamadas em C,. NET precisa de chamadas C, Python precisa de chamadas C, Objective-C precisa de chamadas C. Nenhum deles precisa de chamadas em C ++, que é o ponto que eu estava tentando enfatizar.
DevSolar 5/05
@DevSolar Um monte de coisas do Android são escritas nativamente em Java e não usam JNI (você pode usá-lo "para trás" para chamar código Java de C, mas isso apenas confirma ainda mais que é Java). Não tenho experiência com iOS / OSX, mas ouvi dizer que são os mesmos com o Objective-C.
User253751 5/05
3
@immibis: Mas você faz saber a diferença entre um kernel do sistema operacional e seu espaço de usuário, não é? Eu duvido seriamente que um espaço de usuário principalmente em Java torne o Android menos um kernel Linux com chamadas de sistema no estilo C do que o kernel Linux executando na minha área de trabalho. Eu também duvido que eles estejam usando Java no kernel ou no middleware. Na verdade, eu sei que eles estão usando C lá. É o problema da galinha e do ovo, do contrário. Isso já foi feito em C antes, e, portanto, é muito mais fácil ainda fazê-lo em C.
DevSolar
6

Deixando de fora os detalhes, outras respostas já fornecem:

A razão pela qual tantas linguagens fornece uma ligação C é que todos os sistemas operacionais * nix e Windows expõem a maior parte de sua API do sistema operacional por meio de uma interface C. Portanto, a implementação da linguagem já precisa interagir com C para poder rodar nos principais Oses. Portanto, é simples também oferecer comunicação direta com qualquer interface C a partir do próprio idioma.

Martin Ba
fonte
5

Não há razão. Se a semântica que você está tentando expressar é fundamentalmente compatível com C e não se parece com modelos, não há razão para você se ligar mais facilmente se a implementação for escrita em C. Na verdade, é basicamente por definição que uma interface C pode ser preenchido por qualquer implementação que possa atender ao contrato binário, incluindo uma implementação em outro idioma. Existem outras linguagens além do C ++ que podem implementar contratos binários em C que podem funcionar dessa maneira.

O que realmente se resume a pessoas que não querem aprender novos idiomas ou idéias que tenham semântica ou recursos realmente úteis, tentando desesperadamente escolher qualquer motivo para permanecer na era dos dinossauros.

DeadMG
fonte
4
Eu acho que você está correto e os que recusaram entender mal a pergunta, mas, na verdade, sua resposta falha em destacar esse possível mal-entendido: a pergunta não é sobre "uma biblioteca com interface C" vs. "uma biblioteca com interface C ++", é sobre "uma biblioteca escrita inteiramente em C" vs. "uma biblioteca com uma interface C escrita em C ++".
Doc Brown
@ Snowman: Ter ligações problemáticas em C ++ não tem absolutamente nada a ver com problemas com a exposição de ligações em C.
DeadMG 4/15
2
Então, você vai escolher um idioma que tenha semânticas e recursos úteis e forçar-se a traduzi-los para uma interface C? Embora classes e modelos possam ser evitados (mas questione por que você está usando C ++ em primeiro lugar), todas as funções com ligação C precisam capturar exceções em vez de permitir que elas escapem para o código C. Sem mencionar que qualquer pessoa que use sua biblioteca compatível com C agora precisa lidar com C ++ e seus confrontos ABI e incompatibilidades de bibliotecas, além dos conflitos de ABI e incompatibilidades de bibliotecas do substrato C.
Prosfilaes
2
@ prosfilaes: é trivial converter exceções em códigos de retorno. Você não precisa evitar o uso de classes, pois elas podem ser facilmente reduzidas para uma API C e não precisa usar modelos em sua implementação. E seus usuários não precisam se importar com conflitos de C ++ ABI, a menos que estejam construindo a partir da fonte; nesse caso, você precisa lidar com o idioma de origem, não importa qual seja.
DeadMG 5/05
4
Fiz uma votação não, porque não se deve necessariamente escrever uma biblioteca com uma API C em C ++, mas porque existem boas razões para usar C além de ser um dinossauro. Se você está escrevendo para uma API na linguagem X, seja C, FORTRAN, COBOL, BLISS ou Java, sempre haverá um tempo em que será mais simples, mais fácil e mais correto escrever nessa linguagem do que escrever em uma fantasia , linguagem mais divertida e interface dos dois.
Prosfilaes
2

Existem dois eixos principais ao fazer interface com outro idioma:

  • os conceitos que a interface pode transmitir: apenas valores? referências? genéricos?
  • como a interface é implementada em "binários" (chamada ABI)

C tem uma vantagem sobre C ++ nessas duas frentes:

  • C possui apenas conceitos simples, que aparecem na maioria dos outros idiomas 1
  • A ABI dos binários C é decidida pelo OS 2

Agora, por que a maioria das linguagens tem um conjunto de conceitos semelhante ao de C pode ser devido a ser "simples" ou "pré-existente"; não importa, porém, o ponto é que eles fazem.

Pelo contrário, o C ++ possui conceitos complexos e a ABI é decidida por cada compilador (embora muitos aderam à Itanimum ABI, exceto no Windows ...). Na verdade, houve uma proposta de Herb Sutter para que os sistemas operacionais corrigissem uma ABI C ++ (por sistema operacional) para resolver parcialmente esse problema. Além disso, deve-se observar que um CFI FFI é possível, D está tentando-o 3 .

1 Exceto variadics ( ...), esses não são simples

2 C possui uma ABI padrão?

3 Interface de código D para C ++ herdado

Matthieu M.
fonte
0

Fundamentalmente, tudo se resume à padronização da ABI. Embora nem C nem C ++ tenham uma ABI padronizada que outras linguagens possam usar para fazer a interface entre binários escritos, C se tornou um padrão de fato, todo mundo sabe disso e todo mundo pode usar as mesmas regras simples que a linguagem possui com relação a tipos e chamadas de função.

O C ++ poderia ter uma ABI padrão, mas Stroustrup disse que não vê a necessidade de uma. Ele também diz que seria difícil obter consenso dos escritores de compiladores (embora eu duvide que - o comitê padrão C ++ emitisse uma ABI semelhante às existentes e os escritores de compiladores simplesmente alterariam a próxima versão de seus compiladores, que ocasionalmente são incompatíveis com binários de qualquer maneira, construído com versões antigas de seus compiladores - lembro-me de recompilar algumas bibliotecas com um novo compilador Sun e descobrir que elas não funcionaram com as antigas)

Você notará que algumas empresas passaram a usar uma ABI padrão, a Microsoft iniciou esse processo com o COM nos anos 90, e hoje elas refinaram isso na ABI do WinRT (não confunda com o outro WinRT que se refere para um tipo de tabela OS) que permite que programas escritos em C # se comuniquem com bibliotecas escritas em C ou C ++ (ou seja, a camada de SO da Microsoft é escrita em C ++, exposta usando o WinRT e consumida por aplicativos C # quando chamam qualquer rotina de tempo de execução do SO)

Não há muito o que alguém possa fazer, a menos que um órgão de padrões acelere e corrija essa situação. A Microsoft obviamente vê o valor e tomou medidas para resolvê-lo em sua plataforma.

Portanto, a resposta é realmente C não fornece ligações de idioma. Acontece que ninguém os ouviu e os consome independentemente.

gbjbaanb
fonte
-2

Todas as respostas ficam aquém do problema real: a compilação do C ++ apresenta "name mangling", portanto os binários são incompatíveis com as chamadas de função "simples".

Todo o material da ABI é pouco mais que uma tentativa de padronizá-lo.

Em geral, não é garantido que você possa vincular funções compiladas com diferentes compiladores, mesmo se você seguir o C ++ simples. Antigamente, era certo que eles eram incompatíveis, mas hoje em dia a padronização está se aproximando;)

O OTOH C foi projetado precisamente para ser um "conjunto de alto nível" e permitir todo tipo de interface fácil. Não deve surpreender que seja mais adequado ao gosto entre idiomas.

Nota lateral: o compilador C ++ original (cfront) realmente produziu a fonte C que precisava ser compilada, exatamente como o gcc que produz o Assembly "under the hood".

ZioByte
fonte