31

What are the things that I should keep in mind to write portable code? Since I'm a c++ beginner, I want to practice it since beginning.

Thanks.

understack
  • 11,212
  • 24
  • 77
  • 100
  • 2
    This smells like a Community Wiki question. – bta Jun 23 '10 at 16:38
  • 4
    @bta: Why, exactly? If someone spend 30 minutes writing a good answer to this question, don't they deserve the rep when it is upvoted? – jalf Jun 23 '10 at 16:55
  • 1
    Where do you want to port it? there is a world of differences if you plan porting it to different operative systems, different architectures, so there is an answer for every port, for example working in 8 or even 16 bits tiny embedded systems you should avoid the use of any of the libraries recomended here, so, could you be more specific? – Hernán Eche Jun 23 '10 at 17:46
  • 4
    @jalf- Community Wiki doesn't mean contributors don't deserve credit. It only means that there is no single "correct" answer (as here, the question is a subjective request for general advice). Every response contributes to a collective answer to the question that is outside the scope of any single contribution. You can still upvote/downvote responses to CW questions. – bta Jun 23 '10 at 17:54
  • 1
    @bta: If you make a question community wiki, contributors **get** no credit. You get no rep if your CW answer is upvoted. So by definition, it means that "contributors don't deserve credit". If the actual effects of CW are not desirable, don't make the question CW. – jalf Jun 23 '10 at 20:02
  • @jalf- It doesn't mean they don't /deserve/ credit, only that they don't /get/ credit. But this discussion is horribly off-topic from the question and needs to move to meta.stackoverflow – bta Jun 23 '10 at 21:08
  • 5
    So you're saying contributors *deserve* credit, but shouldn't get it? And why? Just so we can get an arbitrary "Community Wiki" label next to it? What do anyone gain from that? Why is that a good thing? We'd be giving up something good (rewarding people for writing good answers), so there should be some other benefit to compensate for this. What is that? The **only** advantage is that the CW mafia gets a warm, fuzzy feeling inside. And no, the discussion belongs anywhere people suggest actually CW'ing a question. – jalf Jun 23 '10 at 21:37

12 Answers12

19
  • learn to use the standard library
  • read books (eg. this one)
  • when you're experienced, learn to use boost
Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 4
    Also, books like these: http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list – Mike Seymour Jun 23 '10 at 16:37
  • 2
    So, how exactly boost is related to portability? – SigTerm Jun 23 '10 at 16:49
  • 7
    @SigTerm: one of boost's major design goals is portability. it will work on a wide variety of platforms, and does a good job of abstracting away the differences. For instance, the threading libraries and the filesystem libraries work in a platform-agnostic way. Some of this stuff is included in C++0x. – rmeador Jun 23 '10 at 17:04
  • I think that to be fair I have to add that IMO Boost's filesystem library has a few gotchas, like the path interface, which is unnecessarily templatized instead of providing access to the system's native path datatype. – Philipp Jun 23 '10 at 17:47
  • @rmeador: Boost is just one project, and it is not included into C++ standard library. Plus it is quite big. I do not think that "use boost" is a good advice for improving portability. Downvoting. – SigTerm Jun 23 '10 at 19:17
  • 1
    @SigTerm: boost is not just one project, it is many projects, and it likely dwarfs the standard library. it is so widely used (because it provides tons of useful functionality in a cross-platform way) that it has become a de facto standard, and in fact several of its modules are slated for inclusion in the actual standard library when C++0x is released (many compilers/libraries already support the boost constructs under the std namespace). But, downvote away, it doesn't hurt my rep :P – rmeador Jun 23 '10 at 19:44
  • @rmeador: "and it likely dwarfs" I believe that software should be as simple as possible, but not simpler. Boost doesn't follow this principle. It is a bloatware - huge non-standard component, unsuitable for small project. Because it is non-standard component, advice to use it is useless when you're talking about generic language. Plus, some people treat it almost as a religion, which means they will use it even when it is unsuitable. "it doesn't hurt my rep". I don't care whose rep is hurt. – SigTerm Jun 23 '10 at 21:09
  • 2
    @Sig: While its becoming increasingly interdependent, Boost still consists of many seperate libraries of which you can just use some. Some of those, like e.g. `shared_ptr`, should be a de-facto standard unless in-house alternatives are already available. Anyway, why do you think its unsuitable for small projects? Just include what you use, just pack the relevant parts if you need to distribute source. – Georg Fritzsche Jun 24 '10 at 20:58
17

What are the things that I should keep in mind to write portable code?

  1. Keep several compilers nearby, test code regularly on target platforms. If you're doing cross-platform software for windows windows/linux , keep around mingw, visual studio express (i.e. "microsoft compiler"), and a linux installation with g++ (or use virtual machine). Even if your code is perfect, compiler might have some kind of unexpected quirk. For example, certain versions of ms compiler have a limit on sizes of string constants, which gcc doesn't have.
  2. Do not rely on sizes of standard types. For example, on msvc sizeof(wchar_t) is 2 bytes. On linux installation it can be 4 bytes. Use sizeof (if you need it), or try to avoid having to use size of any type in your code. And you should not assume that pointer is 4 bytes large (passing user data pointer into api call scenario) - it will be 8 bytes on 64 bit.
  3. Do not use compiler-specific pragmas, macros and extensions. For example, avoid "#pragma once".
  4. Do not use extensions to standard library (provided by compiler developer). This more applicable to C library functions, though. For example, MS compiler provides multiple "safe" (like strcpy_s) versions of standard C-style routines. Which, of course, won't be available on other platforms.
  5. Be very careful if you decide to use C-style routines (like sprintf) in C++ code. (I know that it is supposed to be a bad practice, but in some scenarios this is useful) They have slightly different implementations, extensions, and different number of parameters. For example, sprintf may have different additional formats that are implemented differently on different platforms. For example, last time I checked "%S" behaves differently on msvc and gcc in vswprintf routine.
  6. Do not rely on compiler-specific data types, like __int32. It is very likely that you'll need some kind of type that is guaranteed to be 4 bytes long (or something like that) - use typedef combined with conditional compilation ("#ifdef WIN32"). OR use types provided by cross-platform library. For example, SDL provides types like Uint8, Qt 4 has quint32, etc. This is pretty common practice.
  7. Avoid direct OS calls. Use standard functions for accessing files.
  8. When you have to use OS-specific calls, use conditional compilation (#ifdef WIN32, etc)
  9. Try to use same build system on all platforms. There is no MSBuild on linux. Use gnumake, cmake, scons or qmake. While in some of those systems you will have to code in flags for different compiler, it will be possible to use same script everywhere. FOr example, it works nicely with SConstructs. And maintaining one building script for all platforms may be easier than synchronizing changes across different build systems.
  10. For all operations that require interaction with operating system (Gui, file manipulation), use cross-platform libraries. Qt is a good choice.
SigTerm
  • 26,089
  • 6
  • 66
  • 115
  • +1 for some good points. Actually there is an MSBuild on Linux, but probably no one uses it for C++ code: http://www.mono-project.com/Microsoft.Build ... and when you are choosing "standard" functions, be aware that there is more than one standard to choose from, and not all standard functions are portable, as I recently learned: http://stackoverflow.com/questions/9896411/why-are-standard-library-function-names-different-between-windows-and-linux – Qwertie Mar 27 '12 at 20:42
  • 1
    Re item 8 I would add, don't scatter #ifdef's through your code: Have a 'platform.h' or similar, and wrap platform-dependent code into platform-independent functions and concentrate those together. In other words, extend your compiler and library with the missing portability features that your code needs, then write the rest of your code portably. – Spike0xff Dec 21 '12 at 18:25
15

Keep platform-specific code separate from reusable code, preferably in a different file but at least in a different function. If you start having #if WIN32 and #if CYGWIN and #if BSD all over the place you'll have a maintenance nightmare.

Then, compile on at least two different platforms early and often. Typical choices are Visual C++ on Windows and gcc on Linux. Since neither the system libraries nor the compiler is shared, you'll catch non-portable code before it becomes deeply entrenched in your design.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
5

Write command-line programs to begin with. When you're ready, find a cross-platform windowing toolkit such as Qt.

If you're interested in writing multilingual code, use a third-party unicode library such as ICU rather than relying on platform-specific libraries.

  • 2
    … And if you handle text at all, care about multilingual. – Steven R. Loomis Jun 23 '10 at 16:40
  • 2
    ICU is a very good point since C++ has no real (Unicode-aware) string datatype. On Unix-like systems `std::string` often works, on Windows `std::wstring` always works, but for really OS-independent programs you need a real string datatype like ICU's `UnicodeString`. – Philipp Jun 23 '10 at 17:37
4

Use STL types when possible. Be careful of using system dependent types and APIs. For example don't use types like UINT, and DWORD on Windows.

You can use a library like boost to make it easier for you to write portable code. If you need a GUI consider using a cross platform toolkit like Qt.

Sometimes you will need to write platform specific code, and in those cases you can do something like this:

#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#endif
Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
3

Others said it before, but here is my view on it:

1) Do you need C++? It's not the best language for writing portable code because it's close to the bare metal. Java, Python, Perl, PHP or Javascript might be better for you.

2) If you need C++, don't try to write completely portable code, it's almost impossible anyway. Instead, decide early which platforms you want to support. For example: Linux, MacOS X, Windows

3) Make sure you test your code on all selected platforms continously. Don't just build on Windows and expect to just compile a Linux version 'when it's done'. Compile on all platforms daily and make sure you keep testing them for problems.

Stijn de Witt
  • 40,192
  • 13
  • 79
  • 80
  • 1
    Along with #2, it is generally easier to go from compiling on 2 platforms to compiling on > 2, than it is to go from 1 to 2. Adding a second platform will help you find the majority of the portability problems. – KeithB Jun 23 '10 at 19:07
  • 1
    +1 for item #1. C++ is one of the worst choices if you want portability, almost every other language, even Bash or Visual Basic, is more portable. – Philipp Jun 24 '10 at 09:00
  • 2
    Sometimes being closer to "the bare metal" is more portable than being high, when I write code for embedded system, C without libraries is the most portable language, why? because there are compilers for every platform and it works in tiny enviroments, C++ is the next one, but there is not compiler for every micro, so, I will ask again, it depends on "Where do you want to port it?" – Hernán Eche Jun 24 '10 at 12:05
  • @HernánEche the reason C++ is not that portable is due to the different compilers. Visual C++ and GCC often disagree on a lot of things specially when it comes to newer C++ standards (ie C++0x, C++1z etc) – trve.fahad Jun 20 '18 at 11:28
2

The unwary programmer is likely to walk into a whole lot of traps, that we can attempt to categorize. But let me tell you first: as an absolute it's impossible.

The problem is that even standard-conforming code might not be portable because of a particular compiler issue.

Now here are the main categories that I can think off the top of my head.

Compiler extensions

Like for example the use of variables arrays:

void func(int const n)
{
  int array[n];
}

This is not standard, but many compilers support it nonetheless because it's just practical.

Standard libraries extensions

Many standard libraries implementations provide a std::hash_map which never was specified. If you use it in your code, it's not portable.

The modern trend is to stash this stuff into the std::tr1 namespace so that programmers are aware that this is an extension.

Also be aware that many define typedef or macros that are not generic (for example PRETTY_FUNCTION). No macro is specified by the standard, and very few typedef are.

Platform specific

For example, the size and alignment of int or double is not specified in the standard. If you do bit-twiddling and expect it to have 32 bits, you'll be screwed on 64 bits platforms even without changing your compiler.

Platform API

Our programs are meant to be compiled, and they are often meant to interact with the computer they run on:

  • for access to the hardware
  • for access to the filesystem
  • for access to the screen

You need to find cross-platform portable APIs, or roll your own. Check some libraries in the list below.

Libraries

Most well-written libraries are largely portable, just make sure that they support:

  • the compilers you are interested in
  • the platforms you are interested in

Good libraries involve:

  • Apache (the collection of libraries)
  • Boost
  • Qt (for graphic)
  • ICU (for Unicode handling)

The others you need to review... and that takes time.

I don't think there is a perfect answer there. But since perfect portability is not possible you need to decide which compilers and platform you wish to support.

For the platform, you should begin with Windows and one Linux flavor. For the compilers, pick any two (with Comeau if you can afford it).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

OS-independent code is surprisingly hard to do in C++. Consider this trivial example:

#include <iostream>
int main(int argc, char** argv) {
  std::cout << argv[0] << std::endl;
}

That is perfectly valid C++, still it's nonportable because it won't accept Unicode command line arguments on Windows. The correct version for Windows would be:

#include <iostream>
int wmain(int argc, wchar_t** argv) {
  std::wcout << argv[0] << std::endl;
}

Of course that is again nonportable, working only on Windows and being nonstandard. So in fact you cannot even write a portable main() function in C++ without resorting to conditional compilation.

Philipp
  • 48,066
  • 12
  • 84
  • 109
  • That'll cause trouble on any platform. Use `std::endl` instead, which flushes the buffer and actually produces some output. I can't begin to count all the questions I've seen on SO which boiled down to people not flushing `cout`. – Ben Voigt Jun 23 '10 at 22:29
  • 1
    @Alexandre: Correct, C++ itself doesn't know anything about strings or Unicode, but because Windows uses UTF-16 strings and `wchar_t` is always a 16-bit unsigned integer on Windows (otherwise no program would compile), you can use `wchar_t` if you want Unicode support on Windows. @Ben: Thanks, I'll correct it. – Philipp Jun 24 '10 at 08:57
  • Actually you MUST use wchar_t (aka WCHAR or TCHAR+_UNICODE) if you want Unicode support on Windows. Windows APIs and standard libraries do not recognize 8-bit char strings as UTF-8. This is highly inconvenient for writing portable code (unless you only care about ASCII), since Linux systems don't seem to support wchar_t strings in general (and you wouldn't want to, since wchar_t is 4 bytes and therefore a very inefficient means of storing a string). – Qwertie Mar 27 '12 at 20:47
2

Some guidelines:

  1. Keep the business end of the code and the GUI separate.
  2. Avoid the use of compiler specific crutches (#pragma, etc.)
  3. Use conventional expressions that won't change behavior with compiler/platform instead of cute bit manipulation tricks.
  4. If it touches hardware it belongs in a device driver.
  5. Use data type headers like types.h (uint32_t, etc.).
  6. Use an operating system abstraction layer so you are not calling operating system calls directly.

Sometimes you have to trade off efficiency and performance to gain portability. For example, if your code requires accessing fields out of a buffer you can always cast a packed struct to the buffer pointer. But that is horribly non-portable. So instead you need to use named pointers calculated with offsets -- sometimes with boundary alignment handling code. Not pretty, but portable. Fortunately you can hide a lot of that stuff with judicious use of class interfaces.

Not all code needs to be written that way. If you design your application in a very modular way with well defined boundaries of responsibility then 90-95% of the code can be portable without pain. Then just isolate the 5-10% in a very localized area that would need to be customized for a new platform.

Amardeep AC9MF
  • 18,464
  • 5
  • 40
  • 50
1

For learning, try to avoid books that concentrate on one implementation. In some cases, the introduction or an early chapter will give you some instructions on how to get or use a language implementation; if it mentions more than one implementation you're probably OK.

Get a reference book that's platform-independent. Stroustrup's The C++ Programming Language is a fine reference, although it's not a good book for a beginner to try to learn from. Don't rely on references for a given implementation. MSDN is useful, for example, but its main focus is how to write Windows programs using Visual C++, not how to write programs that will compile and run anywhere.

To write anything really useful, you're going to have to get into nonportable code. Try to get in the habit of separating the user interface code from everything else, since that's where you'll have the least compatibility. The less code you have to change between platforms, the more portable your code is.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
0

a good idea is to use POSIX system calls. that way you don't have to deal with different ways of creating threads or using mutexes and signals.

the problem is that Windows is not exactly POSIX compliant, but there are libraries that implement certain POSIX features, like this one: [1]: http://sourceware.org/pthreads-win32/

proind
  • 1
  • 1
  • 3
    I'd say that using POSIX system calls is the exact opposite of portable code. Instead of that, use high-level libraries like the ones mentioned in the other answers. – Philipp Jun 23 '10 at 17:34
0

If you can, compile all your code with at least two different compilers.

Ben
  • 1,298
  • 11
  • 12