Existem muitos problemas de portabilidade no C ++, o que ocorre apenas por falta de padronização no nível binário.
Eu não acho tão simples assim. As respostas fornecidas já fornecem uma excelente justificativa para a falta de foco na padronização, mas o C ++ pode ser uma linguagem muito rica para ser adequada para competir genuinamente com o C como um padrão ABI.
Podemos entrar na confusão de nomes resultante da sobrecarga de funções, incompatibilidades de vtable, incompatibilidades com exceções que ultrapassam os limites do módulo, etc. Tudo isso é uma verdadeira dor de cabeça, e eu gostaria que eles pudessem ao menos padronizar os layouts de vtable.
Mas um padrão ABI não se resume apenas a produzir dylibs C ++ produzidos em um compilador capazes de serem usados por outro binário criado por um compilador diferente. A ABI é usada em vários idiomas . Seria bom se eles pudessem, pelo menos, cobrir a primeira parte, mas não há como eu ver o C ++ realmente competindo com C no tipo de nível ABI universal tão crucial para criar os dylibs mais amplamente compatíveis.
Imagine um simples par de funções exportadas assim:
void f(Foo foo);
void f(Bar bar, int val);
... e imaginamos Foo
e Bar
eram classes com construtores parametrizados, copiadores, movemos construtores e destruidores não triviais.
Depois, pegue o cenário de um Python / Lua / C # / Java / Haskell / etc. desenvolvedor tentando importar este módulo e usá-lo em seu idioma.
Primeiro, precisaríamos de um padrão de desconfiguração de nomes para exportar símbolos utilizando sobrecarga de funções. Esta é uma parte mais fácil. No entanto, não deveria realmente ser o nome "desconcertante". Como os usuários do dylib precisam procurar símbolos pelo nome, as sobrecargas aqui devem levar a nomes que não parecem uma bagunça completa. Talvez os nomes dos símbolos possam ser como "f_Foo"
"f_Bar_int"
ou algo desse tipo. Teríamos que ter certeza de que eles não podem colidir com um nome realmente definido pelo desenvolvedor, talvez reservando alguns símbolos / caracteres / convenções para o uso da ABI.
Mas agora um cenário mais difícil. Como o desenvolvedor do Python, por exemplo, invoca os construtores de movimentação, construtores de cópia e destruidores? Talvez possamos exportá-los como parte do dylib. Mas Foo
e se e Bar
for exportado em diferentes módulos? Devemos duplicar os símbolos e implementações associados a este dylib ou não? Eu sugiro que sim, já que pode ser realmente irritante muito rápido, caso contrário, comece a ter que se envolver em várias interfaces dylib apenas para criar um objeto aqui, passá-lo aqui, copiá-lo aqui, copiar um ali, destruí-lo aqui. Embora a mesma preocupação básica possa ser aplicada em C (apenas mais manualmente / explicitamente), C tende a evitar isso apenas por natureza, da maneira como as pessoas programam com ele.
Esta é apenas uma pequena amostra do constrangimento. O que acontece quando uma das f
funções acima lança um BazException
(também uma classe C ++ com construtores e destruidores e derivando std :: exception) no JavaScript?
Na melhor das hipóteses, acho que podemos apenas padronizar uma ABI que funcione de um binário produzido por um compilador C ++ para outro binário produzido por outro. Isso seria ótimo, é claro, mas eu só queria salientar isso. Normalmente, acompanhar essas preocupações para distribuir uma biblioteca generalizada que funcione com compiladores cruzados também costuma ser o desejo de torná-la realmente compatível com linguagens cruzadas generalizadas e compatíveis.
Solução sugerida
Minha solução sugerida, depois de tentar encontrar maneiras de usar interfaces C ++ para APIs / ABIs por anos com interfaces no estilo COM, é apenas me tornar um desenvolvedor "C / C ++" (trocadilho).
Use C para criar essas ABIs universais, com C ++ para a implementação. Ainda podemos fazer coisas como funções de exportação que retornam ponteiros para classes C ++ opacas com funções explícitas para criar e destruir esses objetos no heap. Tente se apaixonar por essa estética C de uma perspectiva ABI, mesmo se estivermos totalmente usando C ++ para a implementação. Interfaces abstratas podem ser modeladas usando tabelas de ponteiros de função. É tedioso agrupar essas coisas em uma API C, mas os benefícios e a compatibilidade da distribuição que vem com isso tendem a fazer com que valha a pena.
Então, se não gostamos de usar essa interface diretamente (provavelmente não devemos pelo menos por razões de RAII), podemos agrupar tudo o que queremos em uma biblioteca C ++ vinculada estaticamente que enviamos com o SDK. Clientes C ++ podem usar isso.
Os clientes Python não querem usar a interface C ou C ++ diretamente, pois não há maneiras de torná-las pythonicas. Eles querem agrupá-lo em suas próprias interfaces pythonique, por isso é bom que apenas exportemos uma API / ABI C mínima para facilitar o máximo possível.
Acho que grande parte da indústria de C ++ se beneficiaria disso mais do que tentar enviar de maneira teimosa interfaces de estilo COM e assim por diante. Isso também tornaria nossa vida mais fácil, pois os usuários desses dylibs não precisariam se preocupar com ABIs desajeitadas. C simplifica, e a simplicidade do ponto de vista ABI nos permite criar APIs / ABIs que funcionam naturalmente e com minimalismo para todos os tipos de IFIs.