In the age of multiple available operating system a programmer often faces a problem of choosing the platform for his project. This choice is often not trivial. Sometimes certain functionality is available only on selected platforms, sometimes user tastes change and they migrate from one platform to another (for example Symbian was dropped because of huge interest in Android), many application are written for multiple platforms from the beginning. It is often best to write code that is platform independent and if that is not possible – to somehow manage existence of multiple platforms in the most simple way.
Two approaches to multiplatform programming
There are two main approaches to multplaform programming. The first assumes that you know all programming platforms and in your code you perform the right calls to your operating system API depending on the platform. The second approach assumes that instead of directly using your operating system you should rather use a multiplatform library that hides complexity of using operating system. In the second case the library creates a unified and consistent API for all supported operating systems.
If you want to handle complexity of multiple platform on your own, then you need to use preprocessor directives to perform calls to the right OS API in your code. A good example is the function that loads dynamic link libraries (the “so”, “dll” or “dynlib” files – extension is specific to your platform). To load a dynamic link library you need to write something like this:
//Define a dynamic link library handle specific to the given platform:
#define SHLIB_HANDLE HMODULE
#define INVALID_SHLIB_HANDLE NULL
//Unix, Linux and OSX version
#define SHLIB_HANDLE (void *)
#define INVALID_SHLIB_HANDLE NULL
inline SHLIB_HANDLE load_dynamic_library(const char * fileName)
return dlopen(fileName, 0);
The aforementioned code was simplified in order for the most important elements to be displayed. Processor directives were used to define universal data types such as dynamic link library handle and invalid value of that handle. Then the load_dynamic_library() function is defined. Depending on the operating system it performs calls to a specific OS API function. An application using only SHLIB_HANDLE type, load_dynamic_library() function and INVALID_SHLIB_HANDLE value is going to be multiplatform. Of course in practice you may want to support a big number of platforms. Thanks to the POSIX standard, majority of operating systems provide similar API and the above code should work well with them. If you want to use a non-standard operating system or if you want to handle a functionality that is not within the scope of POSIX, you have to implement multiple versions of your functions. Unfortunately you also need to test your code on multiple platforms.
Use of libraries
Another option is the use of multiplatform libraries. An example of such a library is the standard C++ library. In the modern C++ 14 version it wraps functionality such as file streams, threads and signals. C++ standard library is actually only a definition of an interface. All platforms provide their own implementation of those interfaces. This approach allows you to use the library on all platform in exactly the same way. In case of other libraries the implementation for the most typical platforms is provided by creators of the library.
Unfortunately the scope of the standard library is very limited. If you want to implement anything that is outside of C++ std, you need to do your own research on libraries that cover the functionality that you need.
Comparison of the two methods
Each of the aforementioned approaches has its pros and cons. Main advantages of your own implementation is full control over the code and ability to optimize implementation according to your needs. But the use of libraries tends to a better solution in most of cases because of easiness and much lower effort needed. Among advantages of use of libraries are:
- You don’t need to learn about all operating systems, you only need to learn how to use the library.
- The use of libraries does not require writing your own code that wraps behavior of operating systems.
- Popular libraries tend to be very well designed. Usually they are created by big groups of developers and architects. It’s hard to achieve the same level of quality by yourself.
- Libraries are usually very well tested. Your own code would be new with many potential issues.
- Libraries tend to be very well optimized.
- Libraries are usually very well documented.
- If the library is popular, there may be programmers in the market who alredy know it very well. In this case you save time needed for research and learning.
- Libraries usually support much broader range of operating systems that you need and therefore give you potential for migrating your product to more platforms in the future.
There are many libraries that are already created. Here is a short list of libraries that I worked with and can recommend for usage in certain scenarios:
- Apache Portability Runtime – this is the only C library on this list. APR is a simple C library that concentrates on optimizing your memory allocation by doing it in big chunks. https://apr.apache.org
- Boost – at the moment of writing Boost is the most popular and widely accepted C++ library. Its many sublibraries allow you to access your operating system API indirectly. For example the asio library allows you to use asynchronous input-output operations (highly specific to your operating system) using multiplatform API. You can learn more about Boost at http://www.boost.org.
- Adaptive Communication Environment – a solid wrapper around operating system API. What distinguishes ACE from Boost is that Boost tries to mimic OS API very closely and ACE provides several components that – while having the same external functionality on all platforms – work in a totally different way internally depending on the platform. http://www.cs.wustl.edu/~schmidt/ACE.html.
- POCO – a set of libraries created mainly to simplify the creation of web-related services. https://pocoproject.org.