Main Page | Modules | File List | Globals | Related Pages
POSH was originally designed to make cross-platform library development easier, so the focus on its design and implementation has been dealing with libraries. That said, it's even easier to use POSH in an application than a library, since applications don't have to worry about the inanity of being built as a Windows DLL or not.

One reason that I haven't really put much thought or effort into POSH as a cross-platform application tool is that most people writing cross-platform apps are already depending on or leverage third party libraries. Very few non-trivial cross-platform applications are completely dependency free. Because of this, many applications already use POSH-like facilities provided by their libraries (for example, LibSDL, Qt and GTK+ all provide features similar to POSH's, however they are aimed squarely at the application, not library, developer.

Step 1: Add POSH to Your Project


Adding POSH to your project is trivial: stick posh.h and posh.c somewhere you can get to them, #include the former and, if you're using POSH's utility functions, compile and link to the latter. That's it.

If you do things right, an application that uses your library won't have to know you're using POSH at all.

Step 2: Use POSH Data Types


To leverage POSH's cross-platform exact-sized types, you need to actually use them. No biggie there.

POSH provides type definitions for 8-bit, 16-bit, 32-bit and 64-bit (if available) signed and unsigned integer types. These are in the form posh_u16_t, posh_s32_t, etc. POSH also provides a byte (unsigned 8-bit) type: posh_byte_t. For a full list, look at Basic Types.

If you use these types, you are guaranteed to get native types of the exact given size, not "at least" the given size. This is to ensure that serialization and deserialization works, since you need to be able to count on sizeof(x) to remain constant across platforms.

The posh data types are fairly verbose. For this reason (and to avoid user confusion), you may want to create your own type definitions and simply alias them to the POSH ones:

typedef posh_u16_t my_u16;

Step 3: Use POSH Function Decoration/Signature Macros


Note:
This is only pertinent to library developers)

Step 3a: POSH_PUBLIC_API

Any functions and data exported by your library should have their type or return type wrapped with POSH_PUBLIC_API().

POSH_PUBLIC_API(void) MyLib_Function( void ); POSH_PUBLIC_API(int) MyLib_integer;
POSH_PUBLIC_API() ensures that the appropriate DLL import/export directives are used if your library is built or used as a Windows DLL.

Step 3b: POSH_CDECL, POSH_STDCALL and POSH_FASTCALL

Different compilers have different ways of specifying a function's calling conventions. POSH has wrapped these into the macros POSH_CDECL, POSH_STDCALL and POSH_FASTCALL.

Step 4: Configure POSH's Preprocessor Symbols


Of course, all this magic requires some effort on the part of the library author, but thankfully not that much. In fact, on most systems you don't have to configure anything if you don't care about disabling floating point or being built/used as a Windows DLL.

The only three symbosl a POSH user is responsible for defining are POSH_BUILDING_LIB, POSH_DLL and POSH_NO_FLOAT.

Step 4a: Defining POSH_BUILDING_LIB

(NOTE: This is only pertinent to library developers)

When building a library you should define the preprocessor symbol POSH_BUILDING_LIB before including posh.h. Do this in your source files, not in your public header files!. You do not want this defined inadvertently when a user is trying to link to your library, since this may cause linkage failures on Windows if your library is a DLL.

For example, if your library is called "MyLib", make sure all your source (not header) files define this before including posh.h, for example:

//MYLIB.C #define POSH_BUILDING_LIB #include "mylib.h" //which in turn includes posh.h

Alternatively, if you distribute a project or makefile you can ensure that the appropriate compiler option (e.g. -DPOSH_BUILDING_LIB=1) is set correctly instead of modifying your source code this way.

Step 4b: Defining POSH_DLL

(NOTE: This is only pertinent to library developers)

POSH checks the POSH_DLL symbol to determine if the __declspec(dllexport) or the __declspec(dllimport) directive should be part of the POSH_PUBLIC_API() macro. This is a moot issue on operating systems other than Windows, but under Windows this is very important if you're building a DLL.

The typical way to handle this, especially if you want the ability to build optionally as a statically linked or dynamically linked library, is to have your own preprocessor symbol that the user can define to enable/disable building-as-a-DLL.

For example, if your library is called "MyLib", you might have your own symbol called MYLIB_DLL. A user of your library would define this if they are building your library as a DLL and/or using it as a DLL. Then in your own code you key off this symbol as such:

//MYLIB_H #ifndef MYLIB_H #define MYLIB_H #if defined MYLIB_DLL # define POSH_DLL #else # undef POSH_DLL #endif #include "posh.h" #endif

Step 4c: Defining POSH_NO_FLOAT

POSH provides the ability to serialize/deserialize single and double-precision floating point values. Floating point support is enabled by default.

However, it may be desirable or necessary to disable this feature, for example on platforms that lack native floating point support or which do not have IEEE compliant floating point bit representations. Or you may just find that linking without floating support gives you a marginally smaller executable.

If you want to disable floating point support in POSH, simply define the symbol POSH_NO_FLOAT, either at the top of posh.h or, preferably, in your makefile/project file.

Step 5: Use Endianess Macros


Probably the single most common topic that comes up regarding cross-platform programming is that of endianess assumptions and conversion. For a complete discussion on processor endianess, um, search the Web, because I'm not going to get into the details here.

POSH provides a set of Byte Order Conversion Macros, such as POSH_LittleU16() and POSH_BigS32(), along with 64-bit (if available) and floating point versions, that convert a value in a specific endianess to host-endian format.

NOTE: If you use the endianess macros, you will have to link with posh.c if byte swapping is actually necessary.

Step 6: Examine Configuration Macros


During compilation POSH looks at the local environment (via examining predefined symbols) and tries to figure out what's what. Once it figures things out, it defines numerous constants to give your code chance to react during the build phase.

The constants potentially defined include:

POSH_BIG_ENDIAN POSH_LITTLE_ENDIAN, POSH_64BIT_INTEGER, POSH_64BIT_POINTER, along with a host of CPU and Operating System Symbols.

Just do the appropriate thing in your code based on the above, for example:

#if defined POSH_BIG_ENDIAN DoBigEndianStuff(); #endif #if !defined POSH_64BIT_INTEGER # error My library needs 64-bit integer support! #endif

Note that POSH does not define any compiler macros, since unlike CPU and OS target macros, these are (hopefully) going to be consistent and unique. In addition, if you have code that is compiler specific, the expectation is that you already know how to detect that compiler.

Step 7: (Optional) Use Byte Swapping Functions


In support of the endianess macros, POSH provides a set of Byte Swapping Functions that byte swap 16, 32 and 64-bit (if available) values.

POSH does not have floating point byte swapping functions, since this could theoretically lead to floating-point exceptions on some systems.

The proper way to handle cross-platform floating point is to convert floating point values to integer form and byte swap that value before serialization. For deserialization, simply do the reverse -- read an integer form, byte swap, then load a floating point value from the converted integers. Doing a direct load to a floating point variable then swapping will potentially result in an invalid floating point variable either before or after the swap, depending on the conversion. For more information, see floating point functions.

Step 8: (Optional) Use Serialization Functions


Directly related to the issue of cross-platform endianess is the ability to serialize and deserialize native data in a portable form. This is typically done by arbitrarily choosing a data file endianess then converting all data from host-to-data endianess at serialization time.

POSH provides a set of In Memory Serialization/Deserialization Functions, along with 64-bit (if available) and floating point versions, that will automatically write native data types to memory and read them back in properly. For floating point values, you must convert to/from integer representation first.

Step 9: (Optional) Use POSH_COMPILE_TIME_ASSERT


POSH provides a cross-platform compile time assertion macro. You don't have to use it, but it's there if you want to. POSH itself uses it fairly liberally in posh.h to sanity check the environment. An example of its use might be something like:

/* ensure that 64-bit integers are actually 64-bits **// POSH_COMPILE_TIME_ASSERT(i64,sizeof(posh_i64_t)==8);

Conclusion


That's pretty much it -- follow the above and you're on the way to cross-platform nirvana. Or something.


Generated on Tue Jan 31 18:27:35 2006 for POSH by doxygen 1.3.7