How to make sure that my program will be fully portable?
Making sure to only use libraries that actually exist on all target platforms would be a good start.
Make it standards compliant. At least a common subset of the standard that is implemented by vendors on all platforms you intend to deploy your application on.
Factor out platform specific portions from platform-independent ones. Typically, the lowest layer or two should deal with the platform.
Keep abreast with changes of:
- Platform/OS APIs
- Tool chains
- Language features
Test, Deploy. Rinse and repeat.
It is impossible. What happens when I write my operating system that has a weird C compiler?
That said, to be portable, you need to:
- Avoid Win32
- Avoid POSIX (which is annoying... You may want to just use Cygwin to provide Windows support)
- Avoid any platform specific library. This usually limits you in graphics to wxWindows, GTK, and QT.
- TEST. Make sure it works.
- Don't assume anything. Windows is weird and uses \r\n, so be careful about that.
- I think Visual C++ on windows gives you warnings about "unsafe c functions" and asks you to use the "safe ones", which are not standard. Don't fall for Microsoft's attempt to monopolize your program.
Some things will help:
- Autoconf will allow any decent system (ie one that includes a shell) to detect common portability issues and set up the correct headers
- Cmake can do this as well, but only on platforms that Cmake itself is available on
Develop on the most restrictive compilation environment. Use the smallest set of features from C++. Split the platform-dependent portions of code into separate files. Develop a configuration (make) environment for each platform, as part of the software package.
Your question:
How to make sure that my program will be fully portable?
cannot be answered satisfyingly. You cannot, in any real world application, make sure it's portable. You can only prove your expectation by accurate tests of the application on the target platform, as has been already proposed here by Lou Franco.
In the process of developing and testing in parallel on different platforms or environments, everyone of us finds his bag of tricks and explores his share of pitfalls. You said in one comment you work on a windows system. This is fine. Try to get your program working with the Visual Studio compiler (and environment). Then, install CygWin with the GCC 4.x compiler suite. Install the Netbeans IDE & C++ Environment and create a project based on the same sources. Netbeans will use the Cygwin GCC 4.x. If your compiled program works with both toolchains, you mastered probably about 90% of the real world portability hurdles.
Regards
rbo
0 Test
This is a necessary but not a sufficient condition for doing anything properly. To test portability, you'll want multiple platforms and compilers.
1 Write to the standard, not to your development platform.
This means, only do something if the standard says you can do it. Only expect a particular result if the standard says you can expect it. Only use a library or API if the standard says it exists. The standard is available here (among other places):
http://openassist.googlecode.com/files/C%2B%2B%20Standard%20-%20ANSI%20ISO%20IEC%2014882%202003.pdf
It helps if you assume that:
- CHAR_BIT is equal to 9.
- sizeof(int) is equal to 5 and
int
is a 37 bit type. Or a 16 bit type. - the basic character set is EBCDIC.
- The epoc began in 1721.
time_t
isdouble
And so on. By which I don't mean, write code that relies on those things to be true, I mean write code that will work if they are, and will also work on a sane implementation.
2 Use the most restrictive and pedantic compiler options you can find,
This is the only practical way to give yourself a reasonable chance of achieving (1).
3 Understand that "real compilers" fail to implement the standard correctly or fully, and make some concessions to this fact.
Theoretically, there's nothing non-portable about a C++ program that uses export
. If it's a perfectly good C++ program in every other respect, then it will work on any conforming C++ compiler. But hardly anyone uses a conforming C++ compiler, so there's a de facto common subset of C++ that you'll want to stick to.
4 Understand that the C++ standard provides quite a restricted programming environment
Certain things are not portable in standard C++, such as drawing graphics on a screen, since standard C++ has no graphics or GUI API. So there is no such thing as a "fully portable" GUI program written in C++. So you may or may not need to revise your goal, depending what your program is supposed to do.
If your program requires something that simply cannot be done entirely within standard C++, then you can make your program easier to port by encapsulating that behaviour within an interface which you think should be implementable on all platforms you care about. Then set about implementing it for each one. This doesn't result in a "fully portable" program, though, since to me that means a program which you can compile and run unchanged on any conforming C++ implementation. A program which can be ported to most platforms with a C++ compiler, probably, assuming they have a screen and a mouse, with some bespoke programming work, isn't the same thing.
All this can be taken too far, of course. You will probably actually want to assume that CHAR_BIT
is 8 (reading files is madness otherwise), and perhaps even rely on a GUI framework like Qt. But you did say, "fully portable", and one of the main things you need to do to write portable programs is usually to work out how far you're willing to compromise on "fully".
5 Assert what you assume
At compile-time if you can, or runtime otherwise, ensure that if your program requires int to be at least 32 bits (or whatever), then it will fail noisily when it isn't. OK, so comprehensive test coverage would catch cases where your arithmetic silently overflows and gives the wrong answer, but it's hard to write truly comprehensive tests, and anyway the tests might make the same non-portable errors as the code, or some poor sucker who has downloaded your code might not run them all properly.
When you use libraries, you are effectively doing this automatically. You'll #include
some header, and if the library isn't available that will fail immediately. At least, you hope it will - it's conceivable that some other implementation could have a header of the same name which does something radically or subtly different. Radical differences usually result in compilation failures, for subtle differences you can test for preprocessor symbols to identify implementations.
Avoid using platform specific libraries. If you can implement desired functionality using the STL and BOOST only, go ahead.
Know the platforms that you intend to ship for. If some platform convention contradicts the standard, ignore the standard. I'm serious about that. For example, if you use the standard std::ifstream
constructor, which takes a char*
argument, you won't be able to open any files with Unicode filenames on Windows—you must use the nonstandard wchar_t*
overload there. The functionality lost by not being able to open files that are allowed and legal on the platform severely outweighs the portability gained by using only what the standard knows; in the end, it's the functionality that matters, not the adherence to a particular standard.
This is less a direct answer to the question, than an answer in the light of other answers.
You need to balance a requirement for absolute portability against the expectations of platform users - there are different basic HCI/HIG guidelines for Windows, OS X, KDE and Gnome, and none of the portable GUI toolkits will automatically produce the right results in each (some allow you to apply different layouts, which is a start).
The inbetween approach is to have a pure portable core with multiple native GUIs.
It's not necessary (there is a lot of software that succeeds despite ignoring conventions) but it is a trade-off that needs to be considered - in particular if there is an existing strong native application.