1
votes

So i'd like to build my application to be Platform Independent such that it can run on Windows, OSX, Linux etc.

For example one of the main "parts" that is different across the platforms is a Window. On Windows it would use the Win32 API and on OSX, Linux I guess it would use something else.

I have an IWindow.h class which is an interface for interacting with a Window and a MSWindow class that encapsulates all the Win32 functions and extends the IWindow interface.

In the future I would create an OSXWindow class etc etc

So my two trouble points are:

  1. At some point I will need to detect what platform I am running on and instantiate the correct class. I imagine I will have to do some ifdef blocks for the includes and then also the instantiation itself?

    #ifdef RUNNING_WINDOWS
    #include 
    #elif RUNNING_OSX
    #include 
    #endif


    //m_window is declared as IWindow* m_window;
    #ifdef RUNNING_WINDOWS
    m_window = new MSWindow();
    #elif RUNNING_OSX
    m_window = new OSXWindow();
    #endif

Obviously RUNNING_WINDOWS and RUNNING_OSX are made up so if anyone knows actual flags I can lookup that would be appreciated.

  1. My main.cpp entry point is currently on Windows.

    //Main Entry point to windows application
    int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {

And the hInstance must be passed to the MSWindow class. This presents a bit of a problem in that my IWindow interface can't take in an HINSTANCE because HINSTANCE is part of Windows and won't exist on OSX or Linux. Would I have separate entry point functions in my main.cpp blocked by the ifdefs?

And would IWindow have an Init function that takes in different parameters based on ifdefs as well?

#ifdef RUNNING_WINDOWS
bool Init(HINSTANCE* hInstance);
#elif RUNNING_OSX
bool Init(Whatever OSX needs);
#endif

Potentially I'm going about this all wrong so hopefully that's where you can come in and set me down the right path.

Thanks!

EDIT:

In light of the comments below, I'll add some more information about my objectives.

Essentially I have a Core Class that is my application. My main.cpp instantiates an instance of this Core class and that's pretty much it.

The Core class will have an instance of a Window class. The Core shouldn't care at all about what kind of Window it is, it just needs to interact with it.

The Window itself then (from the comments below) I gather would handle the platform independence aspect internally to itself. So the Window might be messy due to #ifdef blocks but the rest of the application and anything that interacts with Window won't care.

The other option and first direction I took was that there were separate classes for each "Type" of Window implementation that derived off of a common IWindow interface. Then the #ifdefs would happen in the Core for which type to instantiate and include. While this separates the implementation across OS's I agree it does clutter the Core and if any other future part of the Application needs to reference the Window, it would also need to have #ifdef blocks which will just lead to messier overall code.

2
why are you reinventing the wheel? There are lots of platform independent windowing, media, ... libraries.KillianDS
PS: And as long as you are writing in C++ ... using #ifdef's instead of classes is a REALLY bad idea... :)paulsm4
I'm re-inventing the wheel because I want to learn how to do it. I know I could just use an existing Window library that is already cross-platform but I'm interested in how it works. @paulsm4 Yes it has to be C++. How would I use Classes rather than #ifdef's? At some point there has to be an ifdef to choose which class to use no? cyco130 Thanks, that's helpful for which preprocessor directives to look for which answers part of my questions.Jon

2 Answers

2
votes

Really, the best way to do this is to encapsulate the entire concept of a Window away by creating some class that wraps a Window. This class could then provide the required interfaces though non-API specific functions, so that something that uses it doesn't even need to know how the Win32 API, or GTK, or whatever other windowing system you're going to use works. In short, you don't want to have to use #ifdef every time you need to initiate a window, but want to hide them inside some other class. I've used the Qt Framework as a very powerful, cross platform framework that does just that, and lets the same C++ be compiled on Windows, Mac, Linux, and other platforms. It provides cross platform standardized access to the Windowing system, network connections, threads, media playback, filesystem access, and much more.

If you really want to reinvent the wheel and write your own wrapper classes, you should look at the compiler specific macros that are defined to make sure the platforms you want to support are in place. Here is one good place to look for what macros are defined on what platforms by what compilers. Some common macros are _WIN32 for Visual Studio compiles and __unix__ for Unix based platforms.

2
votes

I have done something like this, so I can offer a few bits of advice.

  • While it would be okay to call your Window classes MSWindow, OSXWindow, etc., this is really not handy, since every part of the code that ever needs to interact with the Window will need to know which one to use. You should call these classes just Window. Since you will only be compiling one of these in a given platform there is no collision.

  • The macros to check for a given OS:

    • _WIN32 for Windows
    • __APPLE__ for OSX and iOS (I set my own constant to tell these two apart in the pch file )
    • __linux__ for Linux
  • I recommend that you do not add #define with the above all over the place. You'll have more portable code if you put the cross-platform specific code in as few places as possible.

  • You will have different main functions on each platform. WinMain() on Windows, main() on others. I recommend that you put these on different files, and you only reference the file that corresponds to correct platform in your makefile or project file.

  • The HINSTANCE problem that you asked is easily solved. In your WinMain() function you will give the HINSTACE to your Window class. The Window class on Windows will have a static method, let's say setHinstance(), that will store the HINSTACE away in a class variable. Then when a Window instance is created, the HINSTACE will be available without having to change the common methods of this class.

  • As a final comment, you should keep in mind that there are several GUI frameworks that are cross-platform that may work for you. If you want a full featured one, then look at Qt. If you want one that is very simple to use (with some nice features, but not as many as Qt) then have a look at FLTK. Both Qt and FLTK are open source.