Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ cross-platform development to avoid preprocessor directives

I need to maintain a project that supports running on Linux and Windows. Some codes using preprocessor directives like this are fine.

#ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers
#include <windows.h>
#else
#include <unistd.h>
#endif

But some are the actual implementation, which I would like to prevent using preprocessor directives.

void Foo()
{

    #ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers
    code for windows 
    #else
    code for Linux
    #endif

    some common code...

    #ifdef _WIN32 // _WIN32 is defined by Windows 32 compilers
    code for windows again
    #else
    code for Linux again
    #endif

}

So things get convoluted and harder to maintain. Is there any better way?

like image 445
Stan Avatar asked Jan 29 '13 20:01

Stan


4 Answers

The traditional way is to "hide" all the code that is specific to any OS in wrapper functions - you can either do that in complete functions that do a higher level functionality - e.g. have a function that returns all directory entries based on a given path as input, or implement the individual base-functions, e.g. start_read_directory(path), read_dir_entry(), end_read_directory() - that's just an example functionality, the same principle(s) can be applied on almost any system specific functionality. Wrap it enough, and you wouldn't be able to tell what you are programming for.

In essence, you are doing it wrong if you have a lot of #ifdef in the code itself.

like image 120
Mats Petersson Avatar answered Nov 02 '22 00:11

Mats Petersson


Handle the OS specifics from the build system, not the code. For instance, have two versions of Foo.cpp: one that gets compiled on Linux and another on Windows. Ideally, the header file will be common and all function signatures identical.

like image 42
Nemanja Trifunovic Avatar answered Nov 02 '22 00:11

Nemanja Trifunovic


You can use a simplified version of the factory pattern.

Have a common interface

class MyClass
{
public:
    virtual void Foo() = 0;
};

And for each platform you create a specific class

#import <windows.h>
class MyClassWindows : MyClass
{
public:
    virtual void Foo() { /* Do something */ }
};

#import <linux.h>
class MyClassLinux : MyClass
{
public:
    virtual void Foo() { /* Do something */ }
};

Then when you need this class, you use your factory:

class MyClassFactory
{
public:
    static MyClass* create()
    {
        #if defined _WIN32
            return new MyClassWindows();
        #elif defined _LINUX
            return new MyClassLinux();
        #endif
    }
}

There a many variants of this methods, including defining the MyClassFactory::create method in the .cpp of each platform-specific class and only compiling the .cpp for the appropriate platform. This avoids all preprocessing directives, the switching is made by choosing the correct implementation file.

like image 6
J_D Avatar answered Nov 01 '22 22:11

J_D


A common pattern would be to provide system independent header files, and platform-specific implementation files.

Nothing platform specific in the header:

class Foo
{
   ...
};

In two different implementation files, foo_linux.cpp

 Foo::Foo() { .. linux code }

foo_windows.cpp

Foo::Foo() { .. windows code }

and maybe platform independent implementation in foo.cpp

void Foo::plat_independent_function()

Your platform builds then link in foo.cpp and foo_platform.cpp

like image 4
Digikata Avatar answered Nov 02 '22 00:11

Digikata