Tecniche per garantire la compatibilità multipiattaforma (C ++)?

12

Stavo finendo uno dei miei primi progetti C ++ che è (secondo il framework) supposto essere multipiattaforma. Ho sviluppato il progetto completamente in Windows e Visual Studio, pensando che dal momento che le librerie sono tutte multipiattaforma, quindi fare la build OSX "in seguito" sarebbe banale. Questo non è stato il caso, ma il "codice di Windows" non funziona correttamente e ha dovuto correggere alcuni errori di compilazione.

Quali tecniche esistono in anticipo assicurati che il codice sia compatibile con tutte le piattaforme? Sviluppando contemporaneamente tutte le piattaforme, testando il codice su ogni piattaforma contemporaneamente, quando vengono aggiunte nuove funzionalità, piuttosto che sviluppare le diverse versioni della piattaforma una dopo l'altra? (*)

Guardando in particolare, si consiglia di non dipendere dagli strumenti, ma piuttosto dai "processi di sviluppo" che aiutano la compatibilità multipiattaforma, indipendentemente dagli strumenti utilizzati. Come quello (*) sopra.

In particolare sto sviluppando un plug-in VST con WDL-OL ( link ) e alcuni DSP multipiattaforma librerie. I progetti WDL-OL hanno entrambi i progetti VS e Xcode, ma suppongo che i problemi derivino dalle librerie e quindi dalle differenze nei compilatori.

    
posta mavavilj 21.06.2016 - 18:39
fonte

5 risposte

13

Creare codice portatile può essere molto impegnativo.

Prima alcuni consigli ovvi relativi alla lingua:

  • usa lo standard C ++ ed evita accuratamente qualsiasi comportamento indefinito
  • si basa principalmente sulla libreria standard (e sulle librerie portatili come boost )
  • include sempre tutte le intestazioni previste. Non dare per scontato che non ti serva un'intestazione perché è inclusa in un'altra (cioè su un'implementazione specifica !): Questo può causare errori di compilazione
  • evita costrutti supportati dal compilatore ma non garantiti dallo standard C ++ (ad esempio struct anonimo o array di lunghezza variabile ): può causare errori di compilazione.
  • usa le opzioni del compilatore per aiutarti a far rispettare la conformità (disabilitando le estensioni del compilatore specifiche, massimizza il livello dei messaggi di avviso restituiti)
  • tieni presente che non è sufficiente che il codice funzioni: ci sono molte insidie della portabilità dipendenti dall'implementazione: dimensione dei tipi di dati (anche elementari), caratteri che possono essere firmato o non firmato per impostazione predefinita , ipotesi su endianness , ordine di valutazione nelle espressioni quando questi usano effetti collaterali , ecc.

Quindi un paio di consigli sul design:

  • Preferisci l'alternativa standard alle funzionalità equivalenti del sistema operativo
  • Isolare il più possibile l'uso delle dipendenze del sistema operativo (ad esempio, in una funzione wrapper controllata con la compilazione condizionale)

Finalmente i punti delicati:

  • codifica dei caratteri: su molti sistemi puoi fare affidamento su utf8 . Ma per Windows è più delicato in quanto il sistema si aspetta ansi o utf-16. Ovviamente puoi contare su un typedef (come TCHAR ), ma può essere difficile in combinazione con la libreria standard (ad esempio cout vs wcout da utilizzare a seconda se si utilizza char o wchar_t )
  • Se per GUI / graphics / advanced I / O non è possibile trovare una libreria portatile che soddisfi le proprie aspettative / requisiti, progettare l'architettura generale per isolare i componenti specifici del sistema operativo. I wrapper potrebbero non essere sufficienti qui, a causa delle molte diverse interazioni e concetti coinvolti.
  • Approfitta di alcuni modelli di design interessanti come ad esempio la fabbrica astratta (ideale per la progettazione di famiglie di oggetti correlati come nell'interfaccia utente specifica del sistema operativo) o il mediatore (ideale per implementare la collaborazione tra famiglie di oggetti correlati) e li usa insieme alla compilazione condizionale.

Ma questi sono solo consigli. In questa zona non puoi guadagnare certezza.

    
risposta data 21.06.2016 - 22:33
fonte
11

Non c'è nulla che possa garantire che il codice sia compatibile con una piattaforma diversa dalla sua costruzione, esecuzione e test. Pertanto, l'approccio di tutte le persone sane è quello di costruire, eseguire e testare la loro applicazione su ogni piattaforma che proiettano dovrà essere costruita, eseguita e testata.

La Continuous Integration (CI) può alleggerire questo onere un po 'per progetti più piccoli dato che puoi ottenere agenti di build economici o gratuiti per alcune piattaforme (principalmente Linux), eseguire lo sviluppo su Windows e tornare semplicemente a Linux quando c'è un problema.

L'IC OSX è piuttosto complicato però.

    
risposta data 21.06.2016 - 20:21
fonte
9

Se stai chiedendo "processi di sviluppo" e la tua piattaforma di sviluppo principale è Windows con Visual Studio, ti suggerisco di provare a creare il tuo progetto senza "windows.h" incluso. Otterrai un sacco di errori di compilazione che ti indirizzeranno verso molti punti in cui dovrai rifattorizzare il tuo codice. Ad esempio, "DWORD" non sarà #definito e dovrai sostituirlo con uint32_t ovunque (google per stdint.h e troverai utili informazioni sui tipi di interi e le loro definizioni multipiattaforma). Successivamente, dovrai sostituire tutte le chiamate all'API Win32, ad esempio Sleep() con il loro equivalente multipiattaforma (ancora, google è il tuo migliore amico, che mostrerà domande e risposte pertinenti nei siti stack * .com). Probabilmente non riuscirai a trovare tutte le sostituzioni cross-platform rilevanti per il tuo codice e dovrai restituire la tua direttiva include "windows." ma metterla in #ifdef _WIN32 - ulteriori dettagli qui

Continua a fare domande più concrete e ottenere risposte: questo è un suggerimento generale per "quale dovrebbe essere il processo di sviluppo"

MODIFICA 1 Un altro mio consiglio è di usare gcc e / o clang sul tuo computer di sviluppo Windows (insieme a Visual Studio)

    
risposta data 21.06.2016 - 20:42
fonte
3

Dipende dai "alcuni errori di compilazione" che hai citato. Senza sapere cosa fossero, è impossibile essere specifici.

Ho codice multipiattaforma per Windows / Linux / iOS / Android / Mac. Ogni nuova piattaforma ha portato alcuni errori e avvertimenti aggiuntivi quando è stata aggiunta per la prima volta. Imparerai rapidamente quali costrutti portano problemi. O evitali o estrai le differenze in un'intestazione con #ifdef s. Prova mai a #ifdef tra piattaforme all'interno del tuo codice.

Un esempio:

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

crea un'istanza temporanea di MyClass che viene cancellata dopo che myfunction è ritornato. Con alcuni dei miei compilatori C ++, quell'istanza è in lettura / scrittura (e il fatto che sia temporanea e verrà presto distrutta non preoccupa il compilatore). Con gli altri, myfunction deve essere ridefinito per prendere un const MyClass & , o il compilatore si lamenta. Non importa ciò che dice lo standard C ++, o quale compilatore ha ragione e quale è sbagliato. Dopo aver incontrato l'errore un paio di volte so di (a) dichiarare una variabile temporanea di tipo MyClass e passa a myfunction o (b) dichiara il riferimento const in myfunction e usa mutable qui e là per deconsolidare.

Conclusione: accumula esperienza e sviluppa i tuoi standard di codifica.

    
risposta data 21.06.2016 - 20:49
fonte
3

Un possibile modo per aiutare la portabilità potrebbe essere quello di affidarsi a solo su dichiarazioni e caratteristiche fornite da C ++ 11 standard e utilizzando librerie e framework multipiattaforma come POCO & Qt .

Ma anche questo non è a prova di errore. Ricorda l'aforisma

there is no such thing as a portable program, there are only programs which have been successfully ported (to some particular platform)

Con la pratica, la disciplina e tanta esperienza, il porting di un programma su un'altra piattaforma potrebbe di solito essere fatto rapidamente. Ma l'esperienza e il know-how contano molto.

    
risposta data 22.06.2016 - 09:33
fonte

Leggi altre domande sui tag