2009-05-13 17 views
6

Estoy involucrado en el desarrollo de una aplicación de teléfono móvil C++ en las plataformas Symbian. Uno de los requisitos es que debe funcionar en todos los teléfonos Symbian desde teléfonos de 2ª edición hasta teléfonos de 5ª edición. Ahora en todas las ediciones hay diferencias en los SDK de Symbian. Tengo que usar las directivas de preprocesador para compilar condicionalmente código que son relevantes para el SDK para los que la aplicación se está construyendo, como a continuación:Alternativas a las directivas de preprocesador

#ifdef S60_2nd_ED 
    Code 
#elif S60_3rd_ED 
    Code 
#else 
    Code 

Ahora bien, como la aplicación que estoy desarrollando no es trivial que pronto crecerá a decenas de miles de líneas de código y directivas de preprocesador como las anteriores se extenderían por todas partes. Quiero saber si existe alguna alternativa a esto o puede ser una mejor forma de utilizar estas directivas de preprocesador en este caso.

Por favor ayuda.

Respuesta

5

He estado exactamente donde está.

Un truco es que, incluso si va a haber condiciones en el código, no encienda las versiones de Symbian. Hace que sea difícil agregar soporte para nuevas versiones en el futuro, o personalizar para teléfonos que son inusuales de alguna manera. En su lugar, identificar cuáles son las propiedades reales son que usted está confiando en, escribir el código en torno a ellos, y luego se incluyen un archivo de cabecera que hace:

#if S60_3rd_ED 
    #define CAF_AGENT 1 
    #define HTTP_FILE_UPLOAD 1 
#elif S60_2nd_ED 
    #define CAF_AGENT 0 
    #if S60_2nd_ED_FP2 
     #define HTTP_FILE_UPLOAD 1 
    #else 
     #define HTTP_FILE_UPLOAD 0 
    #endif 
#endif 

y así sucesivamente.Obviamente, puede agrupar las características por característica en lugar de por versión, si lo prefiere, tener encabezados completamente diferentes por configuración o cualquier esquema que le convenga.

Tuvimos definiciones para las clases de IU heredadas, también, de modo que había algún código de UI en común entre S60 y UIQ. De hecho, debido a lo que era el producto, no teníamos mucho código relacionado con la interfaz de usuario, por lo que una proporción decente era común.

Como dicen otros, es incluso mejor agrupar el comportamiento variable en clases y funciones cuando sea posible, y vincular diferentes versiones.

[Editar en respuesta al comentario:

Nos trataron muy difícil de evitar hacer cualquier cosa depende de la resolución - afortunadamente la aplicación particular, no hizo esto demasiado difícil, por lo que nuestra interfaz de usuario limitada era bastante genérico. Lo principal en que cambiamos la resolución de la pantalla era para las imágenes de fondo/salpicaduras y cosas por el estilo. Tuvimos un script para preprocesar los archivos de compilación, que sustituyeron el ancho y alto en un nombre de archivo, splash_240x320.bmp o lo que sea. De hecho, generamos a mano las imágenes, ya que no había muchos tamaños diferentes y las imágenes no cambiaban a menudo. El mismo script generó un archivo .h que contiene #defines de la mayoría de los valores utilizados en la generación del archivo de compilación.

Esto es para compilaciones por dispositivo: también teníamos más archivos SIS genéricos que cambiaban el tamaño de las imágenes sobre la marcha, pero a menudo teníamos requisitos sobre el tamaño instalado (ROM a veces era bastante limitado, lo cual importa si su aplicación es parte de la imagen del dispositivo base), y cambiar el tamaño de las imágenes fue una forma de mantenerlo un poco abajo. Para admitir la rotación de la pantalla en N92, Z8, etc., seguimos necesitando versiones verticales y horizontales de algunas imágenes, ya que la relación de aspecto volteada no da tan buenos resultados como cambiar el tamaño a la misma relación o similar ...]

+0

Oye, ¿podría decirme también su enfoque para manejar diferentes resoluciones de pantalla móvil? – ardsrk

15

Bueno ... Eso depende de la naturaleza exacta de las diferencias. Si es posible abstraerlos y aislarlos en clases particulares, entonces puede seguir esa ruta. Esto significaría tener implementaciones específicas de la versión de algunas clases, y cambiar implementaciones enteras en lugar de solo unas pocas líneas aquí y allá.

Tendrías

  • MyClass.h
  • MyClass_S60_2nd.cpp
  • MyClass_S60_3rd.cpp

y así sucesivamente. Puede seleccionar qué archivo CPP compilar envolviendo todo el interior usando #ifdefs como arriba, o mi control en el nivel de compilación (a través de Makefiles o lo que sea) qué archivos están incluidos cuando está compilando para varios objetivos.

Dependiendo de la naturaleza de los cambios, esto podría ser mucho más limpio.

+1

Mi idea exactamente. Polimorfismo para modelar las diferencias de comportamiento. Puede implementar el polimorfismo utilizando la herencia (de esta manera se propuso en su respuesta), pero también utilizando la agregación, facilitada por las clases de Rasgos. – xtofl

+3

Esta es la mejor manera. Use el sistema de compilación para lo que significa; '# ifdef's es la ruta de acceso al código que no se puede mantener. –

1

Puede tratar de definir una interfaz común para todas las plataformas, si es posible. Luego, implemente la interfaz para cada plataforma.

Seleccione la implementación correcta utilizando las directivas de preprocesador.

De esta manera, tendrá la directiva de selección de plataforma en menos lugares en su código (idealmente, en un lugar, explícitamente en el archivo de encabezado que declara la interfaz).

Esto significa algo así como:

commoninterface.h /* declaring the common interface API. Platform identification preprocessor directives might be needed for things like common type definitions */ 
platform1.c /*specific implementation*/ 
platform2.c /*specific implementation*/ 
6

En nuestra empresa escribimos un montón de código de plataforma cruzada (gamedevelopment para Win32/PS3/Xbox/etc).
Para evitar macroses relacionados con plataformas tanto como sea posible generalmente utilizamos los próximos trucos:

  • extracto de código relacionado platfrom-en bibliotecas plataforma de abstracción que tiene la misma interfaz a través de diferentes plataformas, pero no es el mismo de implementación ;
  • código dividido en diferentes archivos .cpp para diferentes plataformas (por ejemplo, "pipe.h", "pipe_common.cpp", "pipe_linux.cpp", "pipe_win32.cpp", ...);
  • use macroses y funciones auxiliares para unificar las llamadas a funciones específicas de la plataforma (por ejemplo: "#define usleep (X) Sleep ((X)/1000u)");
  • usan bibliotecas de terceros multiplataforma.
+1

De forma muy similar a sus sugerencias, también puede agregar incluir directorios de búsqueda específicos para cada compilación, por lo que algo como #include "sound_constants.h" encontrará una de las varias permutaciones de ese archivo. –

+0

Buen consejo! Tengo un poco de miedo a los errores de bajo nivel como las declaraciones de estructuras no coincidentes en diferentes unidades de compilación (la sintaxis "#include" permite compilar rutas de inclusión relativas con la prioridad más alta entre todos los directorios de inclusión y estándar ~ _ ~ '). Sin embargo, un guardia como este: http://www.everfall.com/paste/id.php?xxd85sd9c38g - en los encabezados puede resolver esos problemas ... – Rageous

0

No Idea sobre alternativa, pero lo que puede hacer es usar diferentes archivos para incluir en la versión diferente del sistema operativo. ejemplo

#ifdef S60_2nd_ED

#include "graphics2"

#elif S60_3rd_ED

#include "graphics3"

#else

#include "graphics"

1

Mire SQLite. Ellos tienen el mismo problema. Mueven las cosas dependientes de la plataforma a archivos separados y compilan efectivamente solo las cosas necesarias al tener las directivas de preprocesador que excluyen todo el contenido de un archivo. Es un enfoque ampliamente utilizado.

0

Puede hacer algo así como lo hacen para la definición de ensamblaje en el kernel de Linux. Cada arquitectura tiene su propio directorio (asm-x86, por ejemplo). Todas estas carpetas agrupan los mismos archivos de cabecera de alto nivel que presentan la misma interfaz. Cuando se configura el kernel, se crea un enlace llamado asm que se dirige al directorio asm-arch apropiado. De esta forma, todos los archivos C incluyen archivos como.

1

Existen varias diferencias entre las aplicaciones S60 2nd ed y 3rd ed que no están limitadas al código: los archivos de recursos de la aplicación son diferentes, los formatos gráficos y las herramientas para empaquetar son diferentes, los archivos mmp difieren en muchos aspectos.

Según mi experiencia, no intente automatizarlo demasiado, pero tenga scripts de compilación separados para 2nd ed y 3rd ed. En el nivel de código, las diferencias separadas para las propias clases que tienen una API abstracta común, usan indicadores solo en casos excepcionales.

+0

+1. Solíamos generar automáticamente los scripts de compilación con python para evitar parte de la repetición. Básicamente, la sustitución y los condicionales hicieron la mayor parte; en estos días, tal vez valga la pena probar las plantillas de Django. Por supuesto, la cadena de construcción los procesaría con perl en makefiles. Me hacen creer que eventualmente hay un compilador involucrado ;-) –

0

Debería intentar y evitar la difusión de #ifs a través del código.

En cambio; use el #if en los archivos de encabezado para definir macros alternativas y luego en el código use la macro individual.

Este método le permite mantener el código un poco más legible.

Ejemplo:

Plop.h 
====== 

#if V1 
#define MAKE_CALL(X,Y) makeCallV1(X,Y) 
#elif V2 
#define MAKE_CALL(X,Y) makeCallV2("Plop",X,222,Y) 
.... 
#endif 


Plop.cpp 
======== 

if (pushPlop) 
{ 
    MAKE_CALL(911,"Help"); 
} 

para ayudar a facilitar este código específico versión dividida en sus propias funciones, a continuación, utilizar macros para activiate las funciones como se muestra arriba. También puede ajustar las partes cambiantes del SDK en su propia clase para tratar de proporcionar una interfaz coherente, luego todas sus diferencias se gestionan dentro de la clase contenedora dejando su código que hace el trabajo más ordenado.

Cuestiones relacionadas