I have heard that a way to write Cross Platform c++ code is to define classes as follows (for example, a Window class):
window.h
window_win32.cpp
window_linux.cp
A common way to do this is to use polymorphism. You provide an interface that abstracts your functionality without any regard to a specific platform:
class thread
{
virtual ~thread() {}
virtual void run() = 0;
/* whatever */
};
and then you can inherit from this class using platform specific features:
class posix_thread : thread;
at compile time you choose with #ifdef
s what class you include and instantiate.
I would only add a 5) bullet to @Laethnes' answer
5) Write an empty header that includes, at compile-time, the platform header and hide the platform-specific class under a typedef
// MyClass.hpp
#if defined(WINDOWS)
# include "WINMyClass.hpp"
typedef WINMyClass MyClass
#elif defined(OSX)
# include "OSXMyClass.hpp"
typedef OSXMyClass MyClass
... // keep going
#endif
Pros:
Cons:
The point of such an approach is, that you encapsulate OS specific data in the os specific file. If your have to pass around a HWND, then you might reconsider your object design. Wether such a strcuture makes sense depends on how big your os specific code is. You don't really want to squeeze all possible classes into a single file.
On the other hand, there are libraries for GUIs which are doing exactly this - encapsulating the OS specific parts in a library like QT or wxWidgets or others. If you properly separate the GUI from the main code, then you may not even need this approach.
I'm using such a structure in my project to support different versions of xerces, without having the maincode cluttering with #ifdefs
. However in my case, the affected code is rather small. I can imagine that a GUI framework takes much more space.
There is more ways to solve this problem - each has it's pros and cons.
1.) Use macros #ifdef, #endif
// Note: not sure if "WINDOWS" or "WIN32" or something else is defined on Windows
#ifdef WINDOWS
#include <window.h>
#else
// etc.
#endif
class MyClass
{
public:
// Public interface...
private:
#ifdef WINDOWS
HWND m_myHandle;
#else
// etc.
#endif
};
Pros:
Cons:
2.) As was there already written, you might use polymorphism:
// IMyClass.h for user of your class:
class IMyClass
{
public:
virtual ~IMyClass() {}
virtual void doSomething() = 0;
};
// MyClassWindows.h is implementation for one platform
#include <windows.h>
#include "IMyClass.h"
class MyClassWindows : public IMyClass
{
public:
MyClassWindows();
virtual void doSomething();
private:
HWND m_myHandle;
};
// MyClassWindows.cpp implements methods for MyClassWindows
Pros:
Cons:
3.) PIMPL idiom.
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
private:
struct MyClassImplementation;
MyClassImplementation *m_impl;
}
// MyClassWindows.h
#include <windows.h>
#include "MyClass.h"
struct MyClassImplementation
{
HWND m_myHandle;
void doSomething();
}
In this case, MyClassImplementation keeps all needed (at least platform specific) data and implements what is needed (again, platform specific). In MyClass.cpp you include the platform specific implementation (methods can be inline), in constructor (or later if you want to - just be careful) you allocate the implementation and in destructor you will destroy it.
Pros:
Cons:
4.) Define a neutral type, which is big enough to keep your data. For example long long int.
// MyClass.h
class MyClass
{
public:
MyClass();
void doSomething();
private:
typedef unsigned long long int MyData;
MyData m_data;
};
In implementation (e.g. MyClassWindows.cpp) you always need to cast (reinterpret casting) between MyClass::MyData and actual data stored.
Pros:
Cons:
So use the one which is best fitting to your problem... and your personality :3 because with today's power are all four more or less relatively equal in terms of speed and space.
Most of the time, you avoid such things in the header. There are times, however, when you want to expose them. In such cases, my solution has always been to use something like:
#include dependentInclude(syst,MyInclude.lhh)
dependentInclude being a macro which expands to the the path of the file I need, depending on the value of syst (which was set on the command line).