Automatic Mapping of Platform-dependent Datatypes
C and C++ define standard typedefs such as size_t, ptrdiff_t, fpos_t, time_t
, etc., that abstract the underlying type of an object, thereby enabling the compiler to map them to the suitable native datatype automatically. Always use these typedefs in dually-targeted code. Take for example size_t
. In ILP32 it's defined as 32-bit unsigned integer. However, in LP64 it's defined as an unsigned 64-bit integer.
High-level libraries such as <fstream> usually offer a clean migration path because they use abstraction layers. For instance, the tellp() function returns a std::streampos object rather than an int. Likewise, fstream::write() is declared as follows:
typedef size_t streamsize;
write(const char_type* s, streamsize n);
That said, you can still use fundamental types such as int
as loop counters, array indexes, file descriptors, etc.
Several libraries cast pointers to integral types and vice versa. Examples of this include the standard <signal()> function:
handler signal(int signum, handler);
may either be a pointer to a function
or one of the two integral constant SIG_DFL
. The POSIX <dlfcn.h> library
uses this idiom too in its dlsym()
function. The problem is that a pointer's size is platform-dependent; you certainly don't want to store a 64-bit pointer in an int
. The recommended approach is to use the intptr_t
typedefs as integral datatypes that can safely hold a pointer.
An Application Binary Interface (ABI) specifies the binary representation of a programming language's entities, including the name mangling scheme, memory layout, and
the default alignment. Consider the following struct:
On a typical 32-bit system Record
occupies eight bytes. However, on 64-bit systems its size increases to 12 or 16 bytes. Therefore, never assume that the size of a data structure is invariant. Even if it consists of fixed-width data members, the target platform's alignment scheme may still affect its total size. Even abstract classes are subjected to ABI-issues:
int virtual Connect(enum conn_type)=0;
contains no overt data members. Yet as all polymorphic classes, it contains an implicit vptr
whose size is platform dependent. Similarly, the member functions' mangled names are also ABI-dependent. The upside is that accidentally linking 64-bit libraries with 32-bit object files (or vice versa) would fail noisily.
The printf() family of functions uses format flags that control the output's justification, notation, and width. Always use the "%p" flag for pointers, the l- prefix for arguments of types, long and ul- for their unsigned counterparts. Check also that enough room is allowed for output when using either <iostream> or <stdio.h>. Finally, ensure that char buffers are large enough to accommodate up to 20 characters for unsigned long and 18 hex characters for pointer.