Techniques for Ensuring Cross-Platform Compatibility in C++

ccross platformdevelopment-process

I was finishing one of my earliest C++ projects which is (according to the framework) supposed to be cross-platform. I developed the project fully in Windows and Visual Studio, thinking that since the libraries are all cross-platform, then doing the OSX build "later on" would be trivial. This turned out not to be the case, but rather the "Windows code" doesn't run properly and had some compilation errors to be fixed.

What techniques exist to beforehand ensure that the code is compatible with all platforms? Developing all platforms simultaneously, thus testing the code against every platform at the same time, when new features are added, rather than develop the different platform versions one after each other?(*)

Looking specifically advise that's not dependent on the tools, but rather "development processes" that help cross-platform compatibility, regardless of what tools are used. Like the one (*) above.


Specifically I'm developing a VST plug-in with WDL-OL (https://github.com/olilarkin/wdl-ol) and some cross-platform DSP libraries. WDL-OL projects have both VS and Xcode projects set up, but I guess the problems come from the libraries and then differences in compilers.

Best Answer

Creating portable code can be very challenging.

First some obvious language related advices:

  • use standard C++ and avoid carefully any undefined behavior
  • rely primarily on standard library (and portable libraries such as boost)
  • always include all expected headers. Do not assume that you don't need a header because it's included in another one (i.e.on one specific implementation !) : this can cause compilation errors
  • avoid constructs which are supported by compiler but not guaranteed by C++ standard (e.g anonymous struct or variable length array) : can cause compilation errors.
  • use compiler options to help you to enforce compliance (disabling compiler specific extensions, maximize the level of warning messages returned)
  • keep in mind that it's not sufficient that the code works: there are many implementation dependent portability pitfalls : size of (even elementary) data types, characters which can be signed or unsigned by default, assumptions about endianness, order of evaluation in expressions when these use side effects, etc.

Then a couple of design recommendations:

  • Prefer standard library alternative to equivalent operating system functionality
  • Isolate use of operating system dependencies as much as possible (for example, in a wrapper function controlled with conditional compilation)

Finally the delicate points:

  • character encoding: on many systems you can rely on utf8. But for Windows it's more delicate as the system either expects ansi or utf-16. You can of course rely on a typedef (like TCHAR), but this can then be challenging in combination with the standard library (e.g. cout vs wcout to be used depending if using char or wchar_t)
  • If for GUI/graphics/advanced I/O you can't find a portable library that suits your expectations/requirements, design the overall architecture to isolate OS-specific components. Wrappers might not be sufficient here, due to the many different interactions and concepts involved.
  • Take benefit of some interesting design patterns such as for example the abstract factory (ideal for designing families of related objects such as in OS specific UI) or the mediator (ideal to implement collaboration between families of related objects) and use them together with conditional compilation.

But these are only advices. In this area you can't gain certainty.

Related Topic