C++ cross-platform development to avoid preprocessor directives - c++

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?

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.

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.

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.

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

A possibility for the implementation of this is to use the PIMPL idiom, where your class just publishes the "interface" and declares a meaningless pointer to an implementation class (in its dark, hidden and private corner) and the build system takes care of pulling in the correct platform dependent code for the class containing the implementation of your PIMPL.

Related

In practice, is it good to add macros in API headers which way change the layout (or size) of a class?

For example if I have a header like this:
class Example {
int a;
#if TEST
int b;
#endif
};
how can I distribute my binary library, to avoid user getting an ABI problem?
P.S. I asked this because I used a library and it is compiled with the definition TEST, while I'm not define it when I include the header, which leads to the size in header does not match with the library. I'm interesting how to avoid this if I'm going to distribute some libraries someday.
What you could do is to provide a init function for your library and define in the header, and a function like ensure_TEST_is_set that is defined in library/binary.
void ensure_TEST_is_set();
void init_library() {
#if TEST
ensure_TEST_is_set();
#endif
}
void ensure_TEST_is_set() {
#ifndef TEST
throw std::runtime_error("Library not ABI compatible, TEST is not matching");
#endif
}
So the ensure_TEST_is_set is shipped part of the binary, and depending on if TEST was set when compiling or not, it will throw an exception when called.
The #if TEST in the init_library - as it is defined in the header - will be evaluated by everytime the header is used.

C++ reserved symbol as C variable name

I'm using an external C library inside a C++ project.
The header contains a struct with a variable named class:
#ifdef __cplusplus
extern "C" {
#endif
struct something_t {
...
sometype class;
};
#ifdef __cplusplus
}
#endif
g++ does not like this and complains about "error: expected identifier before ';' token".
What options do I have?
I could rename class, but that's cumbersome and breaks upstream compatibility.
I could ask the upstream project to rename the variable, but that may be difficult.
I could redefine class in the header using the preprocessor: #define class class_ Are there any side effects?
Any other suggestions?
What's the best way to handle this situation?
Result: Based on the prevailing preference for option 2, I finally chose to initiate a renaming in the upstream library.
As others already mentioned in comments, the best option is to write another C API layer around that stuff, that uses the other API only internally.
Anything related to this offending struct definition should be exported through opaque pointers only.
In C++ you can use the cleaned up C-API then.
Here's a small sketch:
ThirdParty.h (contains offending code to compile with c++)
#ifdef __cplusplus
extern "C" {
#endif
struct something_t {
...
sometype class;
};
struct something_t* CreateSomething(); // Does memory allocation and initialization
void DoSomething(struct something_t* something);
#ifdef __cplusplus
}
#endif
MyApiWrapper.h
#ifdef __cplusplus
extern "C" {
#endif
typedef void* psomething_t;
struct psomething_t MyCreateSomething(); // Does memory allocation and initialization
void MyDoSomething(psomething_t something);
#ifdef __cplusplus
}
#endif
MyApiWrapper.c
#include "ThirdParty.h"
struct psomething_t MyCreateSomething() {
psomething_t psomething = (psomething_t)CreateSomething();
return psomething;
}
void MyDoSomething(psomething_t something) {
DoSomething((struct something_t*)psomething);
}
Regarding your considered solutions
I could ask the upstream project to rename the variable, but that may be difficult
You certainly should report that bug to let them know. If it's a git-hub hosted project prepare a pull request.
Anyways be prepared that they might not be responsive timely, and you should always have the above mentioned "plan B". It will work regardless ...
I could redefine class in the header using the preprocessor: #define class class_ Are there any side effects?
It could be a viable way, if any place where this particular symbol (class) appears is plain c code and no other parts of the 3rd party c code (e.g. as library) depends on that symbol (which is unlikely).

How to make sure different C++ code base using the same macro?

We are working on two C++ code base, let's call it A and B, the A is an build as an library, and distribute the header files .h and .a file to B.
Let's say there is Lock.h file in A as following:
// Lock.h in code base A
class Lock {
... ...
#ifdef TRACK_THREAD_OWNER_FOR_DEBUG
virtual int GetLockOwner();
#endif
... ...
private:
CriticalSection section;
#ifdef TRACK_THREAD_OWNER_FOR_DEBUG
int threadOwner;
#endif
};
// Caller.cc in code base B
#include "xxx/xxx/Lock.h"
Lock lockObject;
lockObject.Lock();
In code base A, we by default will enable TRACK_THREAD_OWNER_FOR_DEBUG and may change it just before final release day.
We hit some hard bug because TRACK_THREAD_OWNER_FOR_DEBUG are different in A and B, and cause memory corruption because the sizeof(Lock) is different in two library.
So how to protect from this error? Can we trigger an compiler error when build the caller.cc file if the build macro TRACK_THREAD_OWNER_FOR_DEBUG is different in two project?
It is not possible to make this into compiler error, however it should be possible to make this into reasonably clear linker error by exporting some symbol which name depends on the currently defined macros. For example using static guard variable:
// Foo.hpp - library header file
#pragma once
class Foo
{
public: Foo();
#ifdef IMPORTANT_CONDITION
int m_field;
#endif
};
class ConditionGuard
{
public:
ConditionGuard(void) noexcept
{
#ifdef IMPORTANT_CONDITION
CONDITION_ON();
#else
CONDITION_OFF();
#endif
}
#ifdef IMPORTANT_CONDITION
private: static void CONDITION_ON(void);
#else
private: static void CONDITION_OFF(void);
#endif
};
static ConditionGuard const condition_guard{};
// Foo.cpp - library implementation file
#include "Foo.hpp"
Foo::Foo(void) {}
#ifdef IMPORTANT_CONDITION
void ConditionGuard::CONDITION_ON(void) {}
#else
void ConditionGuard::CONDITION_OFF(void) {}
#endif
Now when user code includes library header Foo.hpp it will also trigger construction of condition_guard static variable which will call a library function depending on condition being protected. So if there is a translation unit including Foo.hpp where IMPORTANT_CONDITION is defined differently than in compiled library then there will be a linker error for missing CONDITION_ON or CONDITION_OFF. CONDITION_ON and CONDITION_OFF function names should contain error text.
One option is to just include the full code for A into project B. What are you trying to do by compiling A into a static library?
I think you're best option is to generate different .a files depending the target. i.e. libA_debug.a when TRACK_THREAD_OWNER_FOR_DEBUG is set, libA.a when it is not.
Then you could set the library to link B to based on whether you are compiling a debug or release version.

Structuring Single-File/Header-Only Libraries in C++

Is it considered good practice to structure single-file/header-only libraries in C++ such that they are conditionally either the header or the implementation? For example,
#ifndef LIBRARY_HEADER_HPP_
#define LIBRARY_HEADER_HPP_
// Header
struct Test {
void test();
};
#endif // LIBRARY_HEADER_HPP_
#ifdef LIBRARY_IMPLEMENTATION_
#undef LIBRARY_IMPLEMENTATION_
// Implementation
void Test::test() {
}
#endif // LIBRARY_IMPLEMENTATION_
The user of the library would therefore #define LIBRARY_IMPLEMENTATION before one #include "Library.hpp" in a single implementation file, to avoid multiple definitions.
I've seen this strategy used in C libraries (STB comes to mind), but I was wondering whether this would be considered idiomatic in modern C++ (or if there are better strategies for creating single-file/header-only libraries).

linux- windows cross c++ application

I'm developing an application which has to run on Linux and Windows.
I have an object called obj which I want to use in the code and it has different behavior on Linux and Windows. so I inherit aaa and called WindowsObj for Windows object and LinuxObj for Linux object.
My question is: How to use this object in the code? what do I have to write that it will run both for Linux and Windows?
For swiching types I use typedef like:
typedef uint32_t DWORD;
but what do I have to use for objects?
I want to write this code:
tr1::shared_ptr<WindowsObj> windowsobj (new WindowsObj(parameter));
tr1::shared_ptr<LinuxObj> linuxobj (new LinuxObj(parameter));
Any idea?
The same thing :)
class _object
{
};
class WindowsObject : public _object
{
};
class LinuxObject public _object
{
};
#if defined(WIN32)
typedef WindowsObject Object;
#else
typedef LinuxObject Object;
#endif
Object myObject;
EDIT: Naturally, the interface that WindowsObject and LinuxObject expose must be the same. In this example, _object would be an abstract base-class that defined the interface, and LinuxObject and WindowsObject would then implement this interface, hiding away the platform-specific stuff in their implementation files.
Sample
_object.h
class _object
{
public:
virtual void doSomething() = 0;
}; // eo class _object
WindowsObject.h
#include "_object.h"
class WindowsObject : public _object
{
public:
virtual void doSomething();
}; // eo class WindowsObject
WindowsObject.cpp
#if defined(WIN32)
#include <windows.h>
void WindowsObject::doSomething()
{
// do something totally reliant on windows here
}; // eo doSomething
#endif
Then you would do the same for LinuxObject.h and LinuxObject.cpp, the latter having completely different preprocessor instructions. e.g, #if defined(UNIX) or some such flavor. Note the WIN32 guards around the implementation. Then you'd have some core header file you'd use:
#if defined(WIN32)
#include "WindowsObject.h"
typedef WindowsObject Object;
#else
#include "LinuxObject.h"
typedef LinuxObject Object;
#endif
Now, in your program
Object a;
a.doSomething();
It's worth noting that, if it's just the odd line of code that differs in your complex object (like a init call at initialisation, destruction) you might be better off with a single platform-agnostic Object and put guards in the implementation. This solution makes more sense when there are huge differences.
I tend to use conditional compilation for this, e.g.
#ifdef WIN32
// windows-specific code
#else
// non-windows
#endif
I suggest to use a cross platform framework, like GTK+ or wxWidgets, so that you hadn't to reinvent the wheel...
Then, you should have the least possible platform depended code, and possibly well deep-inside your classes. There you can use #ifdef to conditionally include code for Windows or for Unix.
You may use the same header file with two different implementations in two different .cpp files; the PIMPL idiom makes this possible even if the two implementations require completely different data members. You simply compile and link one source when compiling for Windows and a different one when compiling for Linux.
One area that I've found this useful is for email interfaces. The two OS have completely different methods for sending emails.
Hi if you can use c++, use boost and size_t type its better way.