How SerenityOS declares ssize_t
This post explores one of my favorite hacks in SerenityOS. I don’t recommend doing this in your codebase, but it has worked for us so far. :^)
ssize_t are common types used in many POSIX APIs.
According to POSIX,
they are used as follows:
size_t: Used for sizes of objects.
ssize_t: Used for a count of bytes or an error indication.
ssize_t is essentially a “signed
Since we’re building the whole operating system, including the standard C library ourselves,
we’re also responsible for declaring all the common system types, including
How we declare them
size_t, we leverage the C preprocessor’s predefined
typedef __SIZE_TYPE__ size_t;
However, there is no
__SSIZE_TYPE__ macro for
ssize_t, so I decided to get a little creative:
#define unsigned signed typedef __SIZE_TYPE__ ssize_t; #undef unsigned
Here’s what’s happening: The C preprocessor expands “
__SIZE_TYPE__” to “
unsigned long” or something similar.
We trick it by temporarily defining a macro that replaces “
unsigned” with “
signed”, and so
ssize_t is declared as a signed version of whatever the
size_t type is!
How others declare them
Other C libraries typically use more careful techniques, such as wrapping the declarations in architecture-specific
#ifdef __i386__ typedef uint32_t size_t; typedef int32_t ssize_t; #endif #ifdef __x86_64__ typedef uint64_t size_t; typedef int64_t ssize_t; #endif
That’s obviously a better approach if you’re building with compilers that don’t predefine
__SIZE_TYPE__, or in an environment where
unsigned has already been redefined.
In our case, we haven’t had any issues with our approach yet, so I’m inclined to hold on to the hack as it’s just so cute. :^)