Foros - Stratos

Programadores => General Programadores => Mensaje iniciado por: Marci en 14 de Abril de 2008, 11:44:07 PM

Título: Programación cruzada con C++
Publicado por: Marci en 14 de Abril de 2008, 11:44:07 PM
Estoy tratando de crear una aplicacion multiplataforma (en concreto windows y linux) usando c++. La mayor parte del código compilará en las dos plataformas. Por desgracia hay módulos específicos de cada plataforma. Algo tan sencillo como un timer, por ejemplo.

Hasta ahora la forma de ordenar el código que conozco seria algo como:

class CTimer
{
#ifdef LINUX
...
Codigo especifico de linux
...
#endif

#ifdef WIN32
...
Codigo especifico de windows
...
#endif
}

Organizado asi me resulta bastante lioso y nada intuitivo. Estaba pensando en intentar crear un CTimer_LINUX, un CTimer_WIN y una especie de interfaz CTimer. ¿Sabeis de algún sitio donde pueda encontrar información sobre esto?

Un saludo
Título: Programación cruzada con C++
Publicado por: shephiroth en 15 de Abril de 2008, 12:17:25 AM
Muy buenas. Se que parece un poco estupido, pero, y si utilizas programacion estructurada, creas una cabecera .h, y diferentes .c para cada plataforma??

SUERTE
Título: Programación cruzada con C++
Publicado por: Zaelsius en 15 de Abril de 2008, 12:44:36 AM
Un ejemplo simplificado:

timer.h


class Timer
{
protected:

Timer();

public:

static Timer* Create();

};



timer_linux.h

class TimerLinux : public Timer
{
protected:

TimerLinux();

};




timer_linux.cpp


Timer* Timer::Create()
{
return new TimerLinux();
}

TimerLinux::TimerLinux()
{
...
}





timer_win.h

class TimerWin  : public Timer
{
protected:

TimerWin();

};




timer_win.cpp


Timer* Timer::Create()
{
return new TimerWin();
}

TimerLinux::TimerWin()
{
...
}



Obviamente, en Windows no compilarias timer_linux.cpp/h y en Linux no compilarias timer_win.cpp/h.

De esta manera marcas una interfaz común para ambas implementaciones, y evitas el uso de macros.
Título: Programación cruzada con C++
Publicado por: LC0 en 15 de Abril de 2008, 09:03:21 AM
http://en.wikipedia.org/wiki/Abstract_factory_pattern
Título: Programación cruzada con C++
Publicado por: Prompt en 15 de Abril de 2008, 09:24:52 AM
ZaelSiuS si lo haces así lo que no compila es el código que herede de TimerLinux o TimerWin, como no le metas una macro en el class T : public ...
Título: Programación cruzada con C++
Publicado por: Warchief en 15 de Abril de 2008, 09:31:19 AM
No son necesarios el patrón ni el create (lo mismo). El constructor puede ser público siempre que tenga la misma signatura para todas las plataformas.


// en timer.h
class CTimer
{
 public:
   CTimer();
}

// en linuxTimer.cpp
CTimer::CTimer()
{
 // lo que sea
}

// en winTimer.cpp
CTimer::CTimer()
{
 // lo que sea
}

// en timer.cpp
#if USE_WIN
#include "winTimer.cpp"
#else
#include "linuxTimer.cpp"
#endif


Ya que sólo uno de los cpps estará compilando.
(timer.cpp siempre está incluido en el proyecto, y según los defines, usa uno u otro cpp).


De esa forma se pueden usar objectos CTimer, sin tener que usar memoria dinámica por el patrón.

<edit>
PD: Lo común, si es interesante, puede ir en timer.cpp, si no en el .h.
</edit>
Título: Programación cruzada con C++
Publicado por: Prompt en 15 de Abril de 2008, 09:53:35 AM
Yo creo que lo más logico es lo que dice Warchief. Yo te apoyo Warchief.

#ifdef de includes! de toda la vida! :P
Título: Programación cruzada con C++
Publicado por: [Over] en 15 de Abril de 2008, 10:28:57 AM
Hola.

Tambien lo que se suele hacer es que tener dos ficheros diferentes segun la plataforma y compilas solo la que corresponda.

Es decir, tendrias timer.h y timer.cpp en dos carpetas distintas.

WIN32/timer.h y WIN32/timer.cpp y lo mismo para Linux.

Siempre y cuando ambos .h tengas las mismas funciones publicas que usas en el resto del codigo no tendrias porque ir guarreando el código con ifdef...

Hay quien prefiere tener #ifdef por ahi metidos... yo siempre que puedo prefiero tener ficheros iguales y solo compilo el corresponda según plataforma.

Es otro punto de vista.

Un saludo.
Título: Programación cruzada con C++
Publicado por: Zaelsius en 15 de Abril de 2008, 11:33:51 AM
En mi opinión...


Solución de Warchief:

// en timer.cpp
#if USE_WIN
#include "winTimer.cpp"
#else
#include "linuxTimer.cpp"
#endif


Eso es una guarrada :D , y además si tienes un proyecto para VisualStudio y  otro para Linux (un make p.ej.), te basta con no incluir el .cpp de la otra plataforma para no compilarlo.

Además, aunque la signatura de la clase sea idéntica, lo más probable es que tengas variables miembro distintas para cada implementación. Si no quieres guarrear la declaración de clase con macros y exponer detalles de implementación, no queda otra que tener una interfaz común y derivar de ella.


Solución de Over:

No existe una interfaz común explícita y cuando toques algo en el timer.h de Linux, tendrás que ir a cambiarlo en el de Windows también. En el día a dia, es normal que se te olvide cambiar los ficheros de la otra plataforma al hacer cambios en la tuya.. y hasta que uno de los programadores que trabaja en la otra plataforma no actualiza su repositorio e intenta compilar no se detecta el error.


Solución de LC0 (abstract factory pattern):

Esta me gusta más... pero en ese caso, ya que el tipo de instancia a crear no se resuelve hasta que no ejecutas la aplicación, es necesario compilar y enlazar ambas implementaciones en tu ejecutable. Para cosas del tipo Dispositivo_D3D y Dispositivo_GL puede tener sentido, pero no para código dependiente del SO (no puedes compilar código Linux en Windows, y aunque pudieses no tendría sentido enlazarlo si nunca se va a ejecutar).


Lo mismo me pierdo algo.. (hoy he dormido 4 horas :P)
Título: Programación cruzada con C++
Publicado por: LC0 en 15 de Abril de 2008, 11:56:04 AM
Citar

Esta me gusta más... pero en ese caso, ya que el tipo de instancia a crear no se resuelve hasta que no ejecutas la aplicación, es necesario compilar y enlazar ambas implementaciones en tu ejecutable. Para cosas del tipo Dispositivo_D3D y Dispositivo_GL puede tener sentido, pero no para código dependiente del SO (no puedes compilar código Linux en Windows, y aunque pudieses no tendría sentido enlazarlo si nunca se va a ejecutar).


Razón tienes. Pero puedes hacer que, para cada implementación en concreto, las clases que necesiten dependencias de otra plataforma que no sea la actual, sean vacías. Vamos, que al final entraríamos también a usar #ifdef's y demás :D :


class CTimerLinux : public CTimer
{
   public:
       void hazArgo()
       {
           #ifdef LINUX
               std::cout << "Mira mamá! Estoy haciendo cosas raras en Linux! << std::endl;
           #endif
       }
}

Título: Programación cruzada con C++
Publicado por: davur en 15 de Abril de 2008, 12:20:59 PM
Boost propone una clasificación (http://www.boost.org/community/implementation_variations.html) de mecanismos para tratar con varias implementaciones de una misma interfaz.

En tu caso, te interesa uno que ya se ha mencionado en este hilo:

Cita de: "Boost"Separate files

A library component can have multiple variations, each contained in its own separate file or files. The files for the most appropriate variation are copied to the appropriate include or implementation directories at installation time.

The way to provide this approach in boost libraries is to include specialized implementations as separate files in separate sub-directories in the .ZIP distribution file. For example, the structure within the .ZIP distribution file for a library named foobar which has both default and specialized variations might look something like:

foobar.h                // The default header file
foobar.cpp              // The default implementation file
readme.txt              // Readme explains when to use which files
self_contained/foobar.h // A variation with everything in the header
linux/foobar.cpp        // Implementation file to replace the default
win32/foobar.h          // Header file to replace the default
win32/foobar.cpp        // Implementation file to replace the default

Appropriate: When different platforms require different implementations, or when there are major performance differences between possible implementations.

Not appropriate: When it makes sense to use more that one of the variations in the same installation.
Título: Programación cruzada con C++
Publicado por: zupervaca en 15 de Abril de 2008, 08:42:49 PM
Yo uso otra solucion que es mezcla de varias:

itimer.h
class ITimer
{
public:
  virtual void get() = 0;
};

timer_linux.h
class Timer_Linux : public ITimer
{
  void get(){...};
};

timer_win.h
class Timer_Win : public ITimer
{
  void get(){...};
};

timer_api.h
#ifdef windows
  #include "timer_win.h"
  typedef Timer Timer_win;
#end
#ifdef linux
  #include "timer_linux.h"
  typedef Timer Timer_linux;
#endif

En definitiva ITimer te queda como clase abstracta para crear todas las plataformas que quieras y despues creas una nuevo tipo de dato que sera Timer que es el que maneja el usuario desde el include timer_api.h
Para que queda mas bonito puedes usar namespaces y crear carpetas diferentes, es decir:

system
  time
     api
        win32
           timer.h
        linux
           timer.h
     api.h
     itimer.h
...

Un ejemplo mas claro lo tienes en la libreria multiplataforma y multiapi que he realizado hace unos años.
La mayor ventaja de este sistema es que aunque uses funciones virtuales, estas, son resueltas en tiempo de compilacion ya que solo tendras una clase Timer final.
Otra nota importante antes que se me olvide es que si vas a hacer algo multiplataforma es que llames a todos los archivos que hagas en minusculas, no pongas mayusuclas ya que hay compiladores que no se los tragan, a parte el visual studio a veces te cambie los nombres a minusculas.

Saludos
Título: Programación cruzada con C++
Publicado por: Prompt en 16 de Abril de 2008, 08:52:13 AM
ZaelSiuS si dices que es una warrada, tu solución es un atropello al rendimiento xP

De warrada nada, al contrario es lo más limpio. Tu proposición provoca que tengas que poner #ifdef de todas formas, encima de todo solo te funciona si el interface definido en el .h es el mismo. Y como metas inline a meter más #ifdef.

Lo de incluir en la solución del proyecto unos ficheros y otros no... vais a mantener vosotros el make file! xD con lo facil que es darle a compilarlo TODO y que se quede un makefile de 4 lineas y media.

Para mi, personalmente, 1 de 2, #ifdef de includes, #ifdef de alguna función o pedacito de codigo. Estos 2 ultimos, sin pasarse eh! :D hehehe que sino si que se warrea todo. Si se separa por un poco de código acaba todo duplicado y no es mantenible por 3 lineas de código.

Lo que hace zupervacatambién me gusta es un hibrido válido. Lo que no me gusta es el nombre "_api.h" pero es un ejemplo y bueno se cambia para darle más coherencia. De cara a la programación:

ITimer.h ( o TimerBase.h o TimerInterface.h )
TimerLinux.h TimerWin.h

Timer_api.h == Timer.h

Creo que así queda bastante elegante, optimo y diferenciado. Esta solución está muy bien, zupervacuno! way way! :)

"Enmascarar" los #ifdef en un archivo que no parece que tenga nada de SO, Timer.h y un nombre apropiado para la clase Base o Interface de Linux y Win32.

Un saludete!
Título: Programación cruzada con C++
Publicado por: Buffon en 16 de Abril de 2008, 09:18:00 AM
Cita de: "Prompt"
Lo de incluir en la solución del proyecto unos ficheros y otros no... vais a mantener vosotros el make file! xD con lo facil que es darle a compilarlo TODO y que se quede un makefile de 4 lineas y media.

autotools (http://es.wikipedia.org/wiki/GNU_build_system)
Título: Programación cruzada con C++
Publicado por: Prompt en 16 de Abril de 2008, 09:24:20 AM
Es bonito para IDEs... preSioso!

Hay un IDE deprecated (desgraciadamente) que tenia "Generate MakeFile" era un "makefile" limpio, extremly clean! un gustazo. Nada de decenas de carpetas y este tema...
Título: Programación cruzada con C++
Publicado por: Buffon en 16 de Abril de 2008, 10:55:22 AM
En windows tendrías el proyecto de Visual Studio, de Borland, etc y no necesitarías ningún makefile.

En Mac lo tienes todo con los XCode.

y en Linux ya depende como te lo quieras montar, si prefieres usar un IDE o prefieres montartelo más o menos tu.

Que tienen de bueno las autotools ?[/u]

una vez lo tienes configurado, compilarlo es hacer lo siguiente:


  ./bootstrap
  ./configure
  ./make


Y lo bueno es que gracias a tu script bootstrap y al configure que genera resuelves todas las dependencias de tu proyecto, busca directorio donde tienes las librerías, los includes, busca tu compilador, por que no es lo mismo compilar en Ubuntu, que en Fedora o Red Hat, y mucho menos que compilar en Mac donde también funcionan gracias a los macports :), gracias a autotools tienes un mismo makefile para todas las plataformas.

En la empresa donde trabajo lo utilizamos por que desarrollamos todo para varias plataformas, depende lo que contraten, y es mucho más sencillo iniciar la máquina de plataforma y hacer:


  cvs co PROYECTO
  cd PROYECTO
  ./bootstrap
  ./configure
  ./make


y luego para cada plataforma hacer el instalador.
Título: Programación cruzada con C++
Publicado por: Buffon en 16 de Abril de 2008, 11:00:21 AM
BUG: No se ve el mensaje anterior pero si le doy a post reply si que aparece jeje
Título: Programación cruzada con C++
Publicado por: Prompt en 16 de Abril de 2008, 11:54:14 AM
Hombreee no me digas eso!! HORROR!! como diria gaybrush.

Has mirado este software tan estupendo? http://bitrock.com
Título: Programación cruzada con C++
Publicado por: Buffon en 16 de Abril de 2008, 12:10:53 PM
Cita de: "Prompt"Hombreee no me digas eso!! HORROR!! como diria gaybrush.

Has mirado este software tan estupendo? http://bitrock.com

una cosa es un instalador y otra diferente un creador automatico de makefiles :S

o he leído mal lo que pone en bitrock
Título: Programación cruzada con C++
Publicado por: Prompt en 16 de Abril de 2008, 12:52:52 PM
Lo decia por esto


Cita de: "Buffon"y luego para cada plataforma hacer el instalador.

xP para que lo tengas todo bien unificado hombre! y no tengas que hacer un instaldor por plataforma :P
Título: Programación cruzada con C++
Publicado por: Buffon en 16 de Abril de 2008, 01:05:18 PM
Cita de: "Prompt"Lo decia por esto


Cita de: "Buffon"y luego para cada plataforma hacer el instalador.

xP para que lo tengas todo bien unificado hombre! y no tengas que hacer un instaldor por plataforma :P

$ dpkg-buildpackage -rfakeroot

:P

se que es echarme basura a mi mismo, pero no confío en aplicaciones que no puedo toquetear por dentro xDDD

Cita de: "BitRock"For our projects that are not fully open source, such as our multiplatform installation tool, we provide free, fully-functional licenses to other Open Source projects.
Título: Programación cruzada con C++
Publicado por: davur en 16 de Abril de 2008, 02:04:20 PM
No entiendo la necesidad de mover una decisión claramente estática (tratar diferencias de implementación entre plataformas), resuelta en tiempo de compilación, a un ámbito dinámico. Supone asumir un coste gratuito. Incluso suponiendo que este coste sea despreciable, no entiendo la necesidad de asumirlo en aras de un código más "limpio", que es algo cuanto menos relativo.
Título: Programación cruzada con C++
Publicado por: Marci en 16 de Abril de 2008, 10:10:28 PM
Gracias a todos por las respuestas. En principio la solución que más me gusta es la propuesta por ZaelSiuS (para estar sin dormir eres un fiera  :) ). Pero al mismo tiempo tambien estoy de acuerdo con lo que dice davur:
CitarNo entiendo la necesidad de mover una decisión claramente estática (tratar diferencias de implementación entre plataformas), resuelta en tiempo de compilación, a un ámbito dinámico. Supone asumir un coste gratuito. Incluso suponiendo que este coste sea despreciable, no entiendo la necesidad de asumirlo en aras de un código más "limpio", que es algo cuanto menos relativo.
Tengo que darle más vueltas al tema. Ya os contaré...

Zupervaca, ya le habia echado un ojo a como lo tienes organizado en tu libreria ;-)

Un saludo
Título: Programación cruzada con C++
Publicado por: swapd0 en 21 de Abril de 2008, 11:22:45 PM
//www.wxwidgets.org