views:

459

answers:

12
+11  Q: 

Program portability

How to make sure that my program will be fully portable?

+19  A: 

Continuous integration on all target platforms.

Lou Franco
This. At our office we have hudson nodes on Ubuntu (i386 and x86_64), Mac OS 10.4 (ppc and Intel), 10.5 (intel), 10.6 (intel), Windows XP, Windows Vista, and Windows 7. Each push to the blessed master git repo triggers a build on all platforms and runs unit tests on all platforms.
Grant Limberg
This doesn't (necessarily) result in "fully portable" code. It results in platform-specific code which is specific to a large number of platforms. Whatever "fully portable" means, and there could be some argument to be had about that, it doesn't mean "works on my list of target platforms".
Steve Jessop
+4  A: 

Unit test it, on each platform, during development

EvilTeach
+8  A: 

Avoid platform-specific libraries.

klez
+2  A: 

Making sure to only use libraries that actually exist on all target platforms would be a good start.

ShaderOp
+7  A: 
  • 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.

dirkgently
"At least a common subset of the standard...": that's the great hurdle, working around compiler bugs is just damn hard.
Matthieu M.
Most industry-standard compilers proactively fix bugs you file particularly w.r.t the standard. :) IMO the chance of finding an obvious bug (in a widely used feature of a language) in the compiler is pretty low at least nowadays. YMMV.
dirkgently
+2  A: 

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
mathepic
"Don't assume anything. Windows is weird and uses \r\n, so be careful about that." doesn't std::endl solve this? I'm just asking as I don't really know.
klez
@klez: Merely inserting `\n` will do the right thing, when not in binary mode.
GMan
You have to be careful if the program is designed to interop well between files that can be used on different systems. For example, consider Git - It has to store files using /n in the repository, but on Windows the working copy uses /r/n.
mathepic
@jweyrich: The Git example is the biggest issue with /r/n that you've seen, or /r/n is the biggest issue that you've seen with portability?
mathepic
@mathepic: it was a sarcasm, but nevermind, it wasn't appropriate so I removed my comment.
jweyrich
"Don't assume anything. Windows is weird and uses \r\n" : Actually \r\n is correct as per the ASCII standard (pre-dating all current popular OSes). Everyone else is wrong/weird
James Curran
Really? -1 because I don't have ASCII standard memorized? All I know is that EVERYTHING but Windows uses \n. In that respect, \r\n is the weird one.
mathepic
@mathepic - you seem to think *everything* means "Unix-alikes". Before it became a Unix variant, the Mac used cr without lf - something I first encountered on the Commodore 64 IIRC. And MS-DOS and Windows inherited the CR+LF thing from CP/M and others that followed the standard. FWIW, Unix (or at least Multics) was doing the right thing when it was created - but the ASCII standard wasn't quite finalised at the time. ASCII changed to suit teletypes, but the Unix line end convention was already fixed.
Steve314
@Steve314 By "everything", I mean systems that are commonly used. As far as I know, MS-DOS, OS/2, and the like aren't really in use yet. I'm simply saying that the vast majority of modern OS's use LF. I consider Weird to mean "different from everything or mostly everything else", and therefore Windows is weird in the line ending.
mathepic
@mathepic - "yet" implies they may be in the future, which seems unlikely for the C64 ;-) - If you count platforms, then every platform counts. If it's about user numbers, Windows still trumps all Unix-alikes together, at least on the desktop (and laptop). Windows is still kind-of a de-facto standard operating system, so you may as well call Unix-alikes the freaks. If you're looking to make a more sophisticated analysis, you'll find that there's more *current* platforms out there than just Windows and Unix-alikes.
Steve314
@mathepic - variants of MS-DOS (RTDOS) is still in use on a lot of systems (e.g., ATM machines) and little used things like HTTP still rely on \r\n for line endings.
D.Shawley
*"Autoconf will allow any decent system (ie one that includes a shell)"* ... I can't think of **ANY** interactive operating system that doesn't have a shell. Can you name one? (Hint: Windows has a shell. It's called CMD.EXE.)
JUST MY correct OPINION
As for line endings, the \r\n convention was in use long before UNIX existed. The ASCII standard -- designed for teletypes where a carriage return (CR) and a line feed (LF) were separate and necessary codes -- was finalized in 1963 and updated in 1967 and 1986. UNIX Version 1 was 1971. (Unics was 1969.) So UNIX and its descendants are the non-compliant systems.
JUST MY correct OPINION
Sorry, I should have said a bourne-compatible shell. For windows, you can use cygwin, so I guess that counts as having one.
mathepic
Windows may be the dominant desktop OS, but Unix-a-likes are all over the mobile phone and embedded device space (set-top boxes, etc) and this is only growing. At current rates it won't be too long before Unix devices exceed the PC population - just not in the way anyone predicted. Actually, I'll put my second point (concerning the impossibility of x-platform UI Design) in an answer.
JulesLt
+2  A: 

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.

rwong
The smallest set must be portable across compilers too.Tirelessly and appropriately test it on all platforms.Moreover, making the tests automatic and having exclusive hardware to run them is a plus. This way programmers can't avoid/skip any.
jweyrich
+11  A: 

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

rubber boots
+8  A: 

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 is double

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.

Steve Jessop
The C++ standard leaves a lot of stuff undefined. For example, will the integer literal `50000` fit in an int? Seems obvious that it will, but if you happen to have a compiler for a 16-bit microcontroller around, you might just find it doesn't work. When something as basic as the range of supported values for standard types isn't defined, writing to the standard is important, but too restrictive. Which isn't really disagreeing with you - more adding emphasis to your last paragraph. It's mostly deciding what *kind* of platform you're developing for (desktop, phone, embedded, ...).
Steve314
Good point: checking that `INT_MAX >= 50000L` can help with that, so I've added a point.
Steve Jessop
I'm pretty sure that 50,000 fits within 16bits.
DeadMG
@DeadMG: not in 15 bits, though, and `int` is signed.
Steve Jessop
@DeadMG, Steve - absolutely. Normal int range on 16 bit systems (assuming 2s complement) is -32768 to 32767 inclusive. unsigned int will hold 50,000 but not 100,000 (or for that matter 65,536). I was going to use 32768 as the example, but I figured nice round decimal numbers made the point just as well - I don't need to be right on the boundary.
Steve314
+1 for pedantic compiler warnings!
kotlinski
@Steve: I like your mentioning of the common sense (deciding how far to go) and assertions.
rwong
+2  A: 

Avoid using platform specific libraries. If you can implement desired functionality using the STL and BOOST only, go ahead.

+1 Boost is a great way to get very far towards being platform independent. But it still requires that you use it correctly.
rmeador
+1  A: 

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.

Philipp
`wchar_t` is appropriate for Windows. For some other platforms, UTF-8 (and not UTF-16 or UCS2) should be used.
rwong
Yes, but this means that you have to distinguish between Windows (where you have to use the nonstandard extension) and Unix-like systems. My point is that adhering to the standard would seriously reduce the program's usefulness.
Philipp
A: 

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.

JulesLt