¿Técnicas para garantizar la compatibilidad multiplataforma (C ++)?

13

Estaba terminando uno de mis primeros proyectos de C ++ que, según el marco, se supone que es multiplataforma. Desarrollé el proyecto completamente en Windows y Visual Studio, pensando que dado que las bibliotecas son todas multiplataforma, entonces hacer la compilación OSX "más adelante" sería trivial. Esto resultó no ser el caso, sino que el "código de Windows" no se ejecuta correctamente y tenía que solucionar algunos errores de compilación.

¿Qué técnicas existen para garantizar de antemano que el código sea compatible con todas las plataformas? ¿Desarrollando todas las plataformas simultáneamente, probando el código contra cada plataforma al mismo tiempo, cuando se agregan nuevas características, en lugar de desarrollar las diferentes versiones de la plataforma una tras otra? (*)

Buscando consejos específicos que no dependan de las herramientas, sino más bien de "procesos de desarrollo" que ayuden a la compatibilidad multiplataforma, independientemente de las herramientas que se utilicen. Como el anterior (*).


Específicamente, estoy desarrollando un complemento VST con WDL-OL ( https://github.com/olilarkin/wdl-ol ) y algunas bibliotecas DSP multiplataforma. Los proyectos WDL-OL tienen configurados proyectos VS y Xcode, pero supongo que los problemas provienen de las bibliotecas y luego de las diferencias en los compiladores.

mavavilj
fuente
1
no hay forma de garantizar la portabilidad, solo formas de mejorar / maximizar la portabilidad
phuclv
¿Y por qué no es esto un duplicado? Creo que hubo muchas preguntas similares
phuclv
¿Qué está haciendo tu aplicación? Cómo lo usarías? ¿En la línea de comando, o a través de alguna interfaz gráfica? Por favor, edite su pregunta para mejorarlo.
Basile Starynkevitch
¿Y qué marco estás usando?
Basile Starynkevitch

Respuestas:

15

Crear código portátil puede ser muy desafiante.

Primero algunos consejos obvios relacionados con el lenguaje:

  • use C ++ estándar y evite cuidadosamente cualquier comportamiento indefinido
  • confiar principalmente en la biblioteca estándar (y bibliotecas portátiles como boost )
  • siempre incluya todos los encabezados esperados. No asuma que no necesita un encabezado porque está incluido en otro (es decir, en una implementación específica ): esto puede causar errores de compilación
  • evite construcciones compatibles con el compilador pero no garantizadas por el estándar C ++ (por ejemplo, estructura anónima o matriz de longitud variable ): pueden causar errores de compilación.
  • use las opciones del compilador para ayudarlo a hacer cumplir (deshabilitar extensiones específicas del compilador, maximizar el nivel de mensajes de advertencia devueltos)
  • tenga en cuenta que no es suficiente que el código funcione: hay muchas dificultades de portabilidad dependientes de la implementación: tamaño de los tipos de datos (incluso elementales), caracteres que pueden ser firmados o no por defecto , suposiciones sobre la endianidad , orden de evaluación en expresiones cuando estos usar efectos secundarios , etc.

Luego, un par de recomendaciones de diseño:

  • Prefiero una alternativa de biblioteca estándar a la funcionalidad equivalente del sistema operativo
  • Aísle el uso de las dependencias del sistema operativo tanto como sea posible (por ejemplo, en una función de contenedor controlada con compilación condicional)

Finalmente los puntos delicados:

  • codificación de caracteres: en muchos sistemas puede confiar en utf8 . Pero para Windows es más delicado ya que el sistema espera ansi o utf-16. Por supuesto, puede confiar en un typedef (like TCHAR), pero esto puede ser un desafío en combinación con la biblioteca estándar (por ejemplo, coutvswcout para ser usado dependiendo si se usa charo wchar_t)
  • Si para GUI / gráficos / E / S avanzadas no puede encontrar una biblioteca portátil que satisfaga sus expectativas / requisitos, diseñe la arquitectura general para aislar los componentes específicos del sistema operativo. Las envolturas pueden no ser suficientes aquí, debido a las muchas interacciones y conceptos diferentes involucrados.
  • Aproveche algunos patrones de diseño interesantes como, por ejemplo, la fábrica abstracta (ideal para diseñar familias de objetos relacionados, como en la interfaz de usuario específica del sistema operativo) o el mediador (ideal para implementar la colaboración entre familias de objetos relacionados) y usarlos junto con la compilación condicional .

Pero estos son solo consejos. En esta área no puedes obtener certeza.

Christophe
fuente
-Wall -Wextra -Werror
tubería
1
@pipe También deberías estarlo -pedantic.
5gon12eder
@ 5gon12eder Un muy buen punto en el contexto de esta respuesta.
tubería
11

No hay nada que pueda garantizar que el código sea compatible con una plataforma que no sea construirlo, ejecutarlo y probarlo allí. Por lo tanto, el enfoque de todas las personas sanas es construir, ejecutar y probar su aplicación en cada plataforma que proyecten, será necesario construirla, ejecutarla y probarla.

La integración continua (CI) puede aliviar un poco esta carga para proyectos más pequeños, ya que puede obtener agentes de compilación baratos o gratuitos para algunas plataformas (principalmente Linux), hacer su desarrollo en Windows y simplemente regresar a Linux cuando haya un problema.

Sin embargo, OSX CI es bastante complicado.

DeadMG
fuente
No hay mención, ¿qué es CI?
mavavilj
55
Integración continua: básicamente un grupo de servidores que ejecutan compilaciones y pruebas para usted.
DeadMG
@DeadMG ¿Te refieres a Mozilla Tinderbox?
Damian Yerrick
1
Travis CI ofrece hosts de compilación OSX gratuitos
Daenyth
Solo usa Cmake.
raaj
9

Si está solicitando "procesos de desarrollo" y su plataforma de desarrollo principal es Windows con Visual Studio, le sugiero que intente construir su proyecto sin "windows.h" incluido. Obtendrá muchos errores de compilación que lo llevarán a muchos lugares donde necesitará refactorizar su código. Por ejemplo, 'DWORD' no estará #definido y deberá reemplazarlouint32_t todas partes (google for stdint.hy encontrará información útil sobre tipos enteros y sus definiciones multiplataforma). A continuación, deberá reemplazar todas las llamadas a la API de Win32, como,Sleep()con su equivalente multiplataforma (de nuevo, google es su mejor amigo, quien mostrará preguntas y respuestas relevantes en los sitios stack * .com). Probablemente no tenga éxito en encontrar todos los reemplazos multiplataforma relevantes para su código y tendrá que devolver su include "windows."directiva pero ponerla debajo #ifdef _WIN32. Más detalles aquí

Siga haciendo preguntas más concretas y obteniendo respuestas: esta es una sugerencia general de "lo que debería ser el proceso de desarrollo"

EDITAR 1 Otro mi sugerencia es usar gcc y / o clang en su máquina de desarrollo de Windows (junto con Visual Studio)

mvidelgauz
fuente
3

Depende de los "errores de compilación" que mencione. Sin saber lo que eran, es imposible ser específico.

Tengo código multiplataforma para Windows / Linux / iOS / Android / Mac. Cada nueva plataforma trajo algunos errores y advertencias adicionales cuando se agregó por primera vez. Aprenderá rápidamente qué construcciones traen problemas. Evítelos o abstraiga las diferencias en un encabezado con #ifdefs. Intenta nunca#ifdef entre plataformas dentro de su propio código.

Un ejemplo:

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

crea una instancia temporal de la MyClasscual se elimina una vez que myfunctionha regresado. Con algunos de mis compiladores de C ++, esa instancia es de lectura / escritura (y el hecho de que sea temporal y pronto será destruido no preocupa al compilador). Con otros, myfunctiontiene que redefinirse para tomar un const MyClass &, o el compilador se queja. No importa lo que diga el estándar C ++, o qué compilador es correcto y cuál es incorrecto. Después de encontrar el error un par de veces sé que (a) o bien declarar una variable temporal del tipo MyClassy pasar a que myfunctiono (b) declarar la referencia consten myfunctiony utilizar mutableaquí y allá para deconstify.

En pocas palabras: acumule experiencia y desarrolle sus propios estándares de codificación.

Martin Kochanski
fuente
2
Esa es una característica errónea de MSVC. Si su punto es que el desarrollo en 1 sistema permite que estos se cuelen, punto tomado. Pero esa cosa específica no es algo que deberías estar haciendo.
JDługosz
1
Una buena cosa de usar múltiples cadenas de herramientas es que el subconjunto común que aceptan es más probable que sea C ++ correcto y compatible con los estándares que lo que cada uno de ellos acepta individualmente. En su caso, el código que se muestra es claramente incorrecto según el estándar y ambas soluciones mencionadas son alternativas válidas. Yo diría que importa lo que diga el estándar.
5gon12eder
1
O diría que C ++ multiplataforma es más restrictivo de lo que dice el estándar. En otras palabras, incluso si el estándar lo dice, aún está sujeto a los denominadores comunes más bajos (o, la capacidad de menor proveedor) para las plataformas que debe admitir. Hay muchas bibliotecas de código abierto que tuvieron que excluir, por ejemplo, C ++ 11 porque una fracción de sus componentes (como en los ciudadanos) no son capaces de C ++ 11.
rwong
3

Una posible forma de ayudar a la portabilidad podría ser confiar solo en las declaraciones y características proporcionadas por el estándar C ++ 11 , y mediante el uso de bibliotecas y marcos multiplataforma como POCO & Qt .

Pero incluso esto no es a prueba de fallas. Recuerda el aforismo

no existe un programa portátil, solo hay programas que se han portado con éxito (a alguna plataforma en particular)

Con práctica, disciplina y mucha experiencia, portar un programa a otra plataforma generalmente se puede hacer rápidamente. Pero la experiencia y el saber hacer son muy importantes.

Basile Starynkevitch
fuente
1
Otro consejo es que, cuando la portación es realizada por diferentes personas y equipos, los cambios en el código deben volver a integrarse en la línea principal. De lo contrario, las diferencias de plataforma se convierten en un conocimiento acumulado por el equipo que lo hizo.
rwong