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;
basic_ostream<charT,traits>&
write(const char_type* s, streamsize n);
That said, you can still use fundamental types such as
int and
long as loop counters, array indexes, file descriptors, etc.
Pointer Issues
Several libraries cast pointers to integral types and vice versa. Examples of this include the standard <signal()> function:
handler signal(int signum, handler);
Here,
handler may either be a
pointer to a function or one of the two integral constant
SIG_DFL and
SIG_IGN. 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 and
unintptr_t typedefs as integral datatypes that can safely hold a pointer.
ABI Issues
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:
struct Record{
long idx;
bool cached;
};
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:
class NetInterface
{
public:
int virtual Connect(enum conn_type)=0;
//..additional members
virtual ~NetInterface()=0;
};
NetInterface 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.
I/O Formatting
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.