Técnicas para garantir a compatibilidade entre plataformas (C ++)?

13

Eu estava terminando um dos meus primeiros projetos em C ++, que é (de acordo com a estrutura) supostamente multiplataforma. Desenvolvi o projeto totalmente no Windows e no Visual Studio, pensando que, como as bibliotecas são todas multiplataforma, fazer a compilação do OSX "mais tarde" seria trivial. Isso acabou não sendo o caso, mas o "código do Windows" não funciona corretamente e teve alguns erros de compilação a serem corrigidos.

Quais técnicas existem para garantir antecipadamente que o código seja compatível com todas as plataformas? Desenvolvendo todas as plataformas simultaneamente, testando o código em todas as plataformas ao mesmo tempo, quando novos recursos são adicionados, em vez de desenvolver as diferentes versões da plataforma uma após a outra? (*)

Procurando aconselhar especificamente que não depende das ferramentas, mas "processos de desenvolvimento" que ajudam na compatibilidade entre plataformas, independentemente de quais ferramentas são usadas. Como o (*) acima.


Especificamente, estou desenvolvendo um plug-in VST com WDL-OL ( https://github.com/olilarkin/wdl-ol ) e algumas bibliotecas DSP de plataforma cruzada. Os projetos WDL-OL têm os projetos VS e Xcode configurados, mas acho que os problemas vêm das bibliotecas e depois das diferenças nos compiladores.

mavavilj
fonte
1
não há nenhuma maneira de garantir a portabilidade, apenas maneiras de melhorar / portabilidade de maximização
phuclv
e por que isso não é duplicado? Acredito que havia muitas questões semelhantes
phuclv
O que seu aplicativo está fazendo? Como você usaria isto? Na linha de comando, ou através de alguma interface gráfica? Por favor edite sua pergunta para melhorá-lo.
Basile Starynkevitch
E qual estrutura você está usando?
Basile Starynkevitch

Respostas:

15

Criar código portátil pode ser muito desafiador.

Primeiro, alguns conselhos óbvios relacionados à linguagem:

  • use C ++ padrão e evite cuidadosamente qualquer comportamento indefinido
  • confie principalmente na biblioteca padrão (e em bibliotecas portáteis, como boost )
  • sempre inclua todos os cabeçalhos esperados. Não presuma que você não precisa de um cabeçalho, pois ele está incluído em outro ( por exemplo, em uma implementação específica !): Isso pode causar erros de compilação
  • evitar construções suportadas pelo compilador, mas não garantidas pelo padrão C ++ (por exemplo, estrutura anônima ou matriz de comprimento variável ): pode causar erros de compilação.
  • use opções do compilador para ajudá-lo a impor a conformidade (desabilitando extensões específicas do compilador, maximizar o nível de mensagens de aviso retornadas)
  • lembre-se de que não é suficiente que o código funcione: existem muitas armadilhas de portabilidade dependentes da implementação: tamanho de tipos de dados (mesmo elementares), caracteres que podem ser assinados ou não assinados por padrão , suposições sobre endianidade , ordem de avaliação nas expressões quando estas use efeitos colaterais , etc.

Em seguida, algumas recomendações de design:

  • Prefira a alternativa da biblioteca padrão à funcionalidade equivalente do sistema operacional
  • Isole o uso das dependências do sistema operacional o máximo possível (por exemplo, em uma função de wrapper controlada com compilação condicional)

Finalmente os pontos delicados:

  • codificação de caracteres: em muitos sistemas, você pode confiar no utf8 . Mas para o Windows é mais delicado, pois o sistema espera ansi ou utf-16. Obviamente, você pode confiar em um typedef (como TCHAR), mas isso pode ser desafiador em combinação com a biblioteca padrão (por exemplo, coutvs wcouta ser usado dependendo se estiver usando charou wchar_t)
  • Se, para GUI / gráficos / E / S avançadas, você não consegue encontrar uma biblioteca portátil que atenda às suas expectativas / requisitos, projete a arquitetura geral para isolar componentes específicos do SO. Os invólucros podem não ser suficientes aqui, devido às diversas interações e conceitos envolvidos.
  • Tire proveito de alguns padrões de design interessantes, como por exemplo, a fábrica abstrata (ideal para projetar famílias de objetos relacionados, como na interface do usuário específica do SO) ou o mediador (ideal para implementar a colaboração entre famílias de objetos relacionados) e use-os em conjunto com a compilação condicional .

Mas estes são apenas conselhos. Nesta área, você não pode obter certeza.

Christophe
fonte
-Wall -Wextra -Werror
pipe
1
@ pipe Você também deve estar -pedantic.
5gon12eder
@ 5gon12eder Um ponto muito bom no contexto desta resposta.
pipe
11

Não há nada que possa garantir que o código seja compatível com outra plataforma além de construí-lo, executá-lo e testá-lo lá. Portanto, a abordagem de todas as pessoas sãs é criar, executar e testar seus aplicativos em todas as plataformas que eles projetam, que precisam ser construídas, executadas e testadas.

A Integração Contínua (IC) pode aliviar um pouco esse fardo para projetos menores, pois você pode obter agentes de compilação baratos ou gratuitos para algumas plataformas (principalmente Linux), fazer seu desenvolvimento no Windows e simplesmente retornar ao Linux quando houver um problema.

O OSX CI é bastante complicado.

DeadMG
fonte
Não há menção, o que é CI?
precisa saber é o seguinte
5
Integração contínua - basicamente um monte de servidores que executam versões e testes para você.
precisa saber é o seguinte
@DeadMG Você quer dizer como o Mozilla Tinderbox?
precisa saber é o seguinte
1
O Travis CI fornece hosts de compilação gratuitos para OSX
Daenyth 22/06
Basta usar o Cmake.
raaj
9

Se você está solicitando "processos de desenvolvimento" e sua plataforma de desenvolvimento principal é o Windows com Visual Studio, sugiro que tente criar seu projeto sem o "windows.h" incluído. Você receberá muitos erros de compilação que apontarão para muitos lugares onde você precisará refatorar seu código. Por exemplo, 'DWORD' não será # definido e será necessário substituí-lo por uint32_ttodos os lugares (google for stdint.he você encontrará informações úteis sobre tipos inteiros e suas definições entre plataformas). Em seguida, você precisará substituir todas as chamadas à API do Win32, como,Sleep()com o equivalente em várias plataformas (novamente, o Google é seu melhor amigo, que mostrará perguntas e respostas relevantes nos sites stack * .com). Provavelmente, você não conseguirá encontrar todas as substituições relevantes de plataforma cruzada para seu código e precisará retornar sua include "windows."diretiva, mas colocá-la em baixo #ifdef _WIN32- mais detalhes aqui

Continue fazendo perguntas mais concretas e obtendo respostas - essa é uma sugestão geral para "o que deve ser o processo de desenvolvimento"

EDIT 1 Outra sugestão é usar gcc e / ou clang na sua máquina de desenvolvimento do Windows (junto com o Visual Studio)

mvidelgauz
fonte
3

Depende dos "alguns erros de compilação" mencionados. Sem saber o que eram, é impossível ser específico.

Eu tenho código de plataforma cruzada para Windows / Linux / iOS / Android / Mac. Cada nova plataforma trouxe alguns erros e avisos extras quando foi adicionada pela primeira vez. Você aprenderá rapidamente quais construções trazem problemas. Evite-os ou abstraia as diferenças em um cabeçalho com #ifdefs. Tente nunca #ifdefentre plataformas dentro do seu próprio código.

Um exemplo:

void myfunction(MyClass &);
myfunction(MyClass());

cria uma instância temporária da MyClassqual é excluída após o myfunctionretorno. Com alguns dos meus compiladores C ++, essa instância é de leitura / gravação (e o fato de ser temporário e em breve ser destruído não preocupa o compilador). Com outros, myfunctiondeve ser redefinido para obter um const MyClass &, ou o compilador reclama. Não importa o que o padrão C ++ diz, ou qual compilador está certo e qual está errado. Depois de encontrar o erro de um par de vezes Sei de (a) quer declarar uma variável temporária do tipo MyClasse passar que a myfunctionou (b) declarar a referência constem myfunctione usar mutableaqui e ali para deconstify.

Conclusão: acumule experiência e desenvolva seus próprios padrões de codificação.

Martin Kochanski
fonte
2
Essa é uma característica incorreta do MSVC. Se o seu ponto é que o desenvolvimento de um sistema permite que eles entrem, ponto em questão. Mas essa coisa específica não é algo que você deveria estar fazendo.
JDługosz
1
Uma coisa boa de usar várias cadeias de ferramentas é que o subconjunto comum que elas aceitam tem mais probabilidade de ser C ++ correto e compatível com os padrões do que o que cada uma delas aceita individualmente. No seu caso, o código mostrado está claramente errado pelo padrão e as duas correções mencionadas são alternativas válidas. Eu diria que importa o que o padrão diz.
5gon12eder
1
Ou eu diria que o C ++ multiplataforma é mais restritivo do que o padrão diz. Em outras palavras, mesmo se o padrão diz isso, você ainda está sujeito aos menores denominadores comuns (ou, pelo menos, capacidade do fornecedor) para as plataformas às quais você precisa oferecer suporte. Há muitas bibliotecas de código aberto que tiveram de excluir, por exemplo, C ++ 11, porque uma fração de seus constituintes (como nos cidadãos) não é capaz de C ++ 11.
rwong
3

Uma maneira possível de ajudar a portabilidade seria confiar apenas nas declarações e recursos fornecidos pelo padrão C ++ 11 e usando bibliotecas e estruturas de plataforma cruzada como POCO e Qt .

Mas mesmo isso não é à prova de falhas. Lembre-se do aforismo

não existe um programa portátil, existem apenas programas que foram portados com sucesso (para alguma plataforma específica)

Com prática, disciplina e muita experiência, transferir um programa para outra plataforma geralmente pode ser feito rapidamente. Mas a experiência e o know-how são muito importantes.

Basile Starynkevitch
fonte
1
Outro conselho é que, quando a transferência é feita por pessoas e equipes diferentes, as alterações de código devem ser reintegradas à linha principal. Caso contrário, as diferenças de plataforma se tornarão um conhecimento acumulado pela equipe que o fez.
rwong