9

I'm writing a program involving network I/O, so send and recv are used, which are POSIX functions. They return a ssize_t, which is POSIX-specific too.
The wrappers look like this ATM:

ssize_t sock_send(int sock, const void* msg, size_t len) {
    return send(sock, msg, len, 0);
}

Even though I'm heavily depending on POSIX in my current implementation, I want to make the interface stick closely to the standard because I am planning on writing a Windows implementation later, where POSIX isn't necessarily available (dammit, Windows!).

What would be a good substitution for ssize_t as specified by the C11 standard? Perhaps ptrdiff_t?
Or how should I approach this issue otherwise?

cadaniluk
  • 14,769
  • 2
  • 38
  • 64

2 Answers2

11

If the type ssize_t is not defined, you can just define it yourself. It is supposed to be a signed type with the same size as size_t. Technically, the type ptrdiff_t should not be smaller than size_t, but it could be larger to accommodate for the larger range.

Here is a portable way to define it:

#include <limits.h>
#include <stddef.h>
#include <inttypes.h>
#include <stdint.h>

#if SIZE_MAX == UINT_MAX
typedef int ssize_t;        /* common 32 bit case */
#define SSIZE_MIN  INT_MIN
#define SSIZE_MAX  INT_MAX
#elif SIZE_MAX == ULONG_MAX
typedef long ssize_t;       /* linux 64 bits */
#define SSIZE_MIN  LONG_MIN
#define SSIZE_MAX  LONG_MAX
#elif SIZE_MAX == ULLONG_MAX
typedef long long ssize_t;  /* windows 64 bits */
#define SSIZE_MIN  LLONG_MIN
#define SSIZE_MAX  LLONG_MAX
#elif SIZE_MAX == USHRT_MAX
typedef short ssize_t;      /* is this even possible? */
#define SSIZE_MIN  SHRT_MIN
#define SSIZE_MAX  SHRT_MAX
#elif SIZE_MAX == UINTMAX_MAX
typedef uintmax_t ssize_t;  /* last resort, chux suggestion */
#define SSIZE_MIN  INTMAX_MIN
#define SSIZE_MAX  INTMAX_MAX
#else
#error platform has exotic SIZE_MAX
#endif
chqrlie
  • 114,102
  • 10
  • 108
  • 170
  • 3
    I know at least one platform where this fails: On MSP430X microcontrollers, some toolchains (notably recent versions of the gcc toolchain) use an `uint20_t` for `size_t` and an `int20_t` for `ssize_t`. – fuz Jan 03 '16 at 19:38
  • @FUZxxl: how many bits for the standard types `char`, `short`, `int`, `long` and `long long`? – chqrlie Jan 03 '16 at 20:06
  • @chrqlie 8, 16, 16, 32, 64. `uintptr_t`, `ptrdiff_t`, `intptr_t`, `size_t`, and `ssize_t` all have 20 bit though. – fuz Jan 03 '16 at 20:06
  • @FUZxxl: then indeed the type `ssize_t` cannot be inferred from `SIZE_MAX` and must be had coded. – chqrlie Jan 03 '16 at 20:19
  • When you wrote "ptrdiff_t should not be smaller than size_t" don't you mean "ptrdiff_t should not be smaller than ssize_t" ? – j b Oct 05 '18 at 08:49
  • I mean this: `ptrdiff_t` is a signed type, so you could define `ssize_t` as an alias to `ptrdiff_t` but on some architectures you might get a type larger than `size_t`, that is `sizeof(ptrdiff_t) > sizeof(size_t)`, which is not intended. The problem is you cannot test if `sizeof(ptrdiff_t) == sizeof(size_t)` at the preprocessor level, so you must test numeric values as posted in the answer. – chqrlie Oct 06 '18 at 10:41
  • why use architectures where the number of the native integer bits are not a power of 2? actually, why did they do it? to abuse developers? – neo5003 Jun 08 '19 at 11:39
  • The "is this even possible?" is possible with 16-bit systems that have 32-bit int. – S.S. Anne Apr 22 '20 at 21:48
  • @S.S.Anne: I guess it is possible in theory, but I have never come across an architecture with this combination. Have you? – chqrlie Apr 22 '20 at 22:15
  • I might have, I'm not sure. – S.S. Anne Apr 22 '20 at 22:16
  • "It is supposed to be a signed type with the same size as size_t" seems common, yet I find no spec support for that. [The implementation shall support ... environments in which the widths of ... ssize_t, ... are no greater than the width of type long](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html) maybe useful. – chux - Reinstate Monica Sep 12 '20 at 20:30
  • 2
    @fuz `uint20_t` and 8-bit `char` looks non-compliant as `uintN_t` and `char` should not have padding and bit width of 20 is not a multiple of bit width of 8. Hmmm. – chux - Reinstate Monica Sep 12 '20 at 20:39
  • chqrlie, The `#if SIZE_MAX == UINT_MAX` approach is a reasonable one - UV. Within that _if_ tree, useful to include defines for `SSIZE_MIN` and `SSIZE_MAX`. For pedantic future growth,I'd include `#elif SIZE_MAX == UINTMAX_MAX` – chux - Reinstate Monica Sep 12 '20 at 20:45
  • @chux-ReinstateMonica: good suggestions, answer amended. – chqrlie Sep 12 '20 at 21:08
  • 1
    @chux-ReinstateMonica True, that! They should have called it `__uint20_t` or something similar. – Ian Abbott Jul 08 '21 at 15:46
0

I spy a typo above

#elif SIZE_MAX == UINTMAX_MAX
typedef uintmax_t ssize_t;  /* last resort, chux suggestion */
#define SSIZE_MIN  INTMAX_MIN
#define SSIZE_MAX  INTMAX_MAX

should be

#elif SIZE_MAX == UINTMAX_MAX
typedef intmax_t ssize_t;  /* last resort, chux suggestion */
#define SSIZE_MIN  INTMAX_MIN
#define SSIZE_MAX  INTMAX_MAX

intmax_t (if defined) is supposed to be able to hold any signed integer type, which makes it a good last resort candidate for ssize_t.

uintmax_t (if defined) is supposed to be able to hold any unsigned integer type. Attempting to assign negative numbers to an unsigned integer results in wrapping back around so that the number is positive, which is probably not desired here.

angstyloop
  • 59
  • 5