13

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.

15

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.

  • -Wall -Wextra -Werror – pipe Jun 21 '16 at 23:30
  • 1
    @pipe You should also be -pedantic. – 5gon12eder Jun 22 '16 at 0:57
  • @5gon12eder A very good point in the context of this answer. – pipe Jun 22 '16 at 1:05
11

There is nothing that can guarantee that the code is compatible with a platform other than building it, running it, and testing it there. Therefore, the approach of all sane people is to build, run and test their application on every platform that they project it will need to be built, run, and test on.

Continuous Integration (CI) can ease this burden a fair bit for smaller projects as you can get cheap or free build agents for some platforms (mostly Linux), do your development on Windows, and simply return to Linux when there is a problem.

OSX CI is pretty tricky though.

  • There's no mention, what's CI? – mavavilj Jun 21 '16 at 19:33
  • 5
    Continuous Integration- basically a bunch of servers that run builds and tests for you. – DeadMG Jun 21 '16 at 19:52
  • @DeadMG You mean like Mozilla Tinderbox? – Damian Yerrick Jun 21 '16 at 22:39
  • 1
    Travis CI provides free OSX build hosts – Daenyth Jun 22 '16 at 0:34
  • Just use Cmake. – raaj Jun 26 '18 at 4:20
9

If you are asking for "development processes" and you primary development platform is Windows with Visual Studio then I would suggest to try building your project without "windows.h" included. You will get a lot of compilation errors that will point you to many places where you'll need to refactor your code. For example, 'DWORD' won't be #defined and you'll need to replace it with uint32_t everywhere (google for stdint.h and you will find useful information about integer types and their cross-platform definitions). Next, you will need to replace all calls to Win32 API, such as, Sleep() with their cross-platform equivalent (again, google is your best friend, who will show relevant questions and answers in stack*.com sites). You will probably not succeed to find all relevant cross-platform replacements for you code and you'll have to return your include "windows." directive but put it under #ifdef _WIN32 - more details here

Keep asking more concrete questions and getting answers - this is general suggestion for "what should be development process"

EDIT 1 Another my suggestion is to use gcc and/or clang on your Windows development machine (along with Visual Studio)

3

It depends on the "some compilation errors" you mention. Without knowing what they were, it's impossible to be specific.

I've got cross-platform code for Windows / Linux / iOS / Android / Mac. Each new platform brought some extra errors and warnings when it was first added. You will quickly learn which constructs bring problems. Either avoid them, or abstract the differences into a header with #ifdefs. Try never to #ifdef between platforms within your code itself.

One example:

void myfunction(MyClass &);
myfunction(MyClass());

creates a temporary instance of MyClass which is deleted after myfunction has returned. With some of my C++ compilers, that instance is read/write (and the fact that it is temporary and will soon be destroyed doesn't worry the compiler). With others, myfunction has to be redefined to take a const MyClass &, or the compiler complains. It doesn't matter what the C++ standard says, or which compiler is right and which is wrong. After encountering the error a couple of times I know to (a) either declare a temporary variable of type MyClass and pass that to myfunction or (b) declare the reference const in myfunction and use mutable here and there to deconstify.

Bottom line: accumulate experience and develop your own coding standards.

  • 2
    That's a mis-feature of MSVC. If your point is that developing on 1 system allows these to creep in, point taken. But that specific thing is not something you should be doing. – JDługosz Jun 21 '16 at 21:34
  • 1
    One good thing of using multiple tool-chains is that the common subset they accept is more likely to be correct, standards-compliant C++ than what each of them accepts individually. In your case, the code shown is clearly wrong by the standard and both of your mentioned fixes are valid alternatives. I'd say it does matter what the standard says. – 5gon12eder Jun 22 '16 at 1:02
  • 1
    Or I would say cross-platform C++ is more restrictive than what the standard says. In other words, even if the standard says so, you are still subject to the lowest common denominators (or, least vendor capability) for the platforms which you have to support. There are plenty of open-source libraries that had to exclude say C++11 because a fraction of their constituents (as in citizens) are not C++11 capable. – rwong Jun 22 '16 at 9:38
3

A possible way to help portability could be to rely only on declarations and features provided by the C++11 standard, and by using cross-platform libraries and frameworks like POCO & Qt.

But even this is not fail-proof. Remember the aphorism

there is no such thing as a portable program, there are only programs which have been successfully ported (to some particular platform)

With practice, discipline and a lot of experience, porting a program to another platform could usually be done quickly. But experience and know-how matters a lot.

  • 1
    Another advice is that, when the porting is done by different people and team, that the code changes must be re-integrated into the mainline. Otherwise, the platform differences become a knowledge hoarded by the team that did it. – rwong Jun 22 '16 at 9:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.