Unfortunately, a lot of the work that programmers undertake when writing cross-platform software is redundant, tedious, error prone and, well, boring. The most fundamental, basic tasks -- inferring your current configuration environment, byte order/endian safety routines, import/export function signatures for Windows DLLs, correctly sized data types -- are performed over and over by thousands of programmers around the world.
I ran into this problem when I was trying to extract some of my own code to make it open source. That sub-library was dependent on a lot of bits of unrelated code, mostly having to do with the issues I described earlier. So I did the obvious thing -- I extracted those pieces separately, and made a "libconfig.h", and all was well.
Until I wanted to open source another sublibrary.
Now, was I going to basically copy and rename a bunch of stuff in "libconfig.h", or was I going to figure out a way of sharing libconfig.h between two completely unrelated projects? I opted for the latter, and it was about that time that I realized that no one else has tried to make a cross-platform, project agnostic "libconfig.h".
And thus the Portable Open Source Harness was born.
Once those goals were defined, I then had to determine what specific features I wanted POSH to have. The original idea was to create a set of sized types; define macros that indicated target operating system, CPU and endianess; and proper handling of import/export identifiers when building Windows DLLs.
After discussion with some friends, I decided to add support for in-memory serialization/deserialization, verification and architecture string reporting. These elements required the addition of a single source file. Things were already getting more complicated, but two source files is livable complexity.
But in the end, POSH still basically does two things: compile time configuration management and optional run-time routines for endianess conversion, serialization and verification.
Another key element of POSH's configuration management is the automation of the various magic keywords and linkage specifications necessary to create and use a Windows DLL (specifically, I'm talking about
The final element of POSH's configuration management is defining a set of correctly sized types (including 64-bit integers) that an application can count on.
POSH is not a general cross-platform framework such as SDL, Qt, wxWindows or GTK. It is focused on providing an extremely simple set of features that are useful regardless of the user's domain of interest.
POSH is not an abstraction library. It does not attempt to abstract platform details in a way that isolates the programmer from the underlying system. In fact, POSH does quite the opposite by trying to communicate to the programmer (via the build system) as much about the target and host platforms as possible.
For example, POSH provides a common access mechanism to the compiler's underlying 64-bit integer type, but if the compiler does not support this feature, POSH simply punts, as opposed to trying emulate such support.
Finally, POSH is not religious about how extreme it will be when trying to achieve portability. At some point, you have to just say "screw it" when a platform fights you too much. If a particular architecture or compiler is so idiosyncratic that it breaks the rest of POSH, I will not compromise the cleanliness or functionality of POSH just so we can say we run on yet one more platform.
For this reason, POSH isn't trying to be compatible with every compiler, CPU and operating system combination ever made. It makes a fundamental set of assumptions, and if it finds that those assumptions are invalid (via compile time assertions), it will generate an error during compile time. I simply refuse to make POSH less functional and/or less clean to appease less popular architectures and development tools, even though this may go against the grain of the whole portability argument.
In certain (hopefully) rare instances, POSH_GetArchString() will report an error when a run-time analysis does not match the static analysis of the target operating system's characteristics. You should always check POSH_GetArchString() at least once during development for a quick sanity check.
If an error is encountered, please contact us at email@example.com with the specifics and we'll try to see what went wrong and, if possible, fix it for later versions of POSH.
sizeof( float ) == 4
sizeof( double ) == 8
The size assumptions are necessary for the serialization/deserialization routines. The floating point format assumption is necessary so that if you serialize a float value on one system, it can be loaded successfully on another architecture. Fortunately, the vast majority of systems with functional floating point do adhere to the above conventions.
Just for completeness, the IEEE single-precision (
float) format is:
S EEEEEEEE FFFFFFFFFFFFFFFFFFFFFFF 0 1 8 9 31
The IEEE double-precision (
double) format is:
S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0 1 11 12 63
Minor note: POSH doesn't care about how certain bit patterns are interpreted, so if a system uses the above bit representations but doesn't handle NaN, infinity or denormals correctly, then technically POSH is cool since the serialization/deserialization aspects are still valid.
(For those wondering, some examples of systems that have native floating point but which are not IEEE compliant include DEC VAX; some Cray; some embedded processors; and various mainframe/supercomputer architectures.)
In the event that these assumptions are not valid, you can manually disable POSH's floating point support by defining the symbol POSH_NO_FLOAT globally via a compiler switch, or just by inserting
#define POSH_NO_FLOAT 1 at the top of posh.h.
In theory POSH will work just fine on architectures with, say, 16-bit pointers, however it's never been tested and POSH doesn't give you any way to detect this situation. POSH basically assumes that if it's not a 64-bit architecture, then it must be a 32-bit architecture.
sizeof(void*)==4 compile time assertion in posh.h is hit and you're aware of the ramifications, feel free to remove the assertion and continue using POSH.