Live data from Hacker News

How SerenityOS declares ssize_t

awesomekling.github.io

31–40 of 95 posts

Re: How SerenityOS declares ssize_t

#31
This is a great illustration why C is, on one hand, terrible, and, on the other hand, staunchly practical.

Here the trivial, unthinking preprocessor allows to do a pretty crazy (though very understandable and predictable) thing, which allows to acceptably solve a problem which would take years and a ton of effort to be solved "properly" (with standardization and compiler support).

Re: How SerenityOS declares ssize_t

#32
post #21

Earlier quoted context omitted.

Sure. The C standard has always erred on the side of supporting odd platforms -- see ones complement, decimal floating point, sizeof function pointer != sizeof data pointer, etc. Most platforms today have converged around an approach that doesn't require these escape hatches. But if you're trying to e.g. bring up C on CHERI [1] or other platforms that don't make the assumption that memory is all one big flat address…

> see ones complement I'm glad you brought this up because is kind of my point. C++ realized this was useless baggage and finally left it behind. [1] I don't see why ptrdiff_t is much different here. C just doesn't want to let things to, I guess. Literally any feature you put into a language will end up being used (or abused) by someone for something. "It's nice" that at some point in the future someone can pick up a…

It's a philosophy thing. There are plenty of languages that just solve for flat Von Neumann memory models. In the past, that did not describe all machines -- e.g. the pre-standard Borland C/C++ compilers for x86 real mode that used 32-bit ptrdiff_t and 16-bit size_t (which doesn't count as an answer because they were pre-standard in so many ways). In the future, it may or may not describe all machines -- it's possible that RISC wins forever and we continue to push all complexity into the software, or it's possible that something capabilities based comes forward instead. Similarly in primitive types, perhaps some day with have unums.

If the future is still flat, then... we've paid the cost of an extra few paragraphs in the standard? Folks interested in writing non-portable code can ignore this and use implementation-defined behaviors; those who want to be fully portable to future machines can be more careful (although ptrdiff_t is basically a cursed type anyway, so I don't see this particular overhead mattering). Yes, getting rid of ones complement makes sense these days -- but if you were worried about the compatibility issues around supporting it properly and avoiding undefined behavior, you're probably spending exactly as much effort today dealing with the fact that INT_MIN and friends are cursed mathematically on twos complement machines.

Meanwhile, suppose that we actually break out of this local minima. That's an interesting world, and an even more interesting one if we can carry most of our software forward with us.

We're not even a century into designing computers yet. I can't even begin to predict what architectures will look like in another five or ten centuries -- even assuming that transistor budgets continue to taper off. But I'll say that of the languages I use daily, C is one of the few that I'd still expect to be around and functional in that future, even if only as an archeological curiosity; it allows a decently high fidelity description of how an algorithm should be implemented across more than half a century of hardware.

Re: How SerenityOS declares ssize_t

#33
post #17

Earlier quoted context omitted.

The entire purpose of my question was that "I'm sure they're out there" is as close as I've ever found an answer to this, so I was looking for an actual example, not a hypothetical one.

I mean, a C spec compatible compiler for the MSP430 would be an example (see paragraph 7.20.3), TI has just decided to deviate from the language standard here. It's an example of a platform that would meet the requirements, and chooses to not offer a (compliant) C compiler instead.

I don't understand how it's an example. It sounds like ssize_t and ptrdiff_t need the same number of bits on this architecture. And if ptrdiff_t is wrong, they'd probably make ssize_t wrong too. Also I don't think "we need more distinct constants because then if a compiler author deliberately makes one wrong they might leave the other one alone" is enough justification to have two.

Re: How SerenityOS declares ssize_t

#34

I would do it like this: #if SIZEOF_SIZE_T == 8 typedef int64_t ssize_t #elif SIZEOF_SIZE_T == 4 typedef int32_t ssize_t #elif SIZEOF_SIZE_T == 2 // LOL typedef int16_t ssize_t #else #error port me! #endif SIZEOF_SIZE_T can be obtained using a script which compiles a test program without executing it. Over they years I used more than one approach, settling on this one: https://www.kylheku.com/cgit/txr/tree/configure?…

Rather than a bunch of #if blocks, one could instead do:

    #define SIZE_T_BITS 64
    
    #define PASTE3(a,b,c) a##b##c
    #define TYPEDEF_UINT(bits,name) typedef PASTE3(uint,bits,_t) name
    #define TYPEDEF_INT(bits,name) typedef PASTE3(int,bits,_t) name
    
    TYPEDEF_UINT(SIZE_T_BITS,size_t);
    TYPEDEF_INT(SIZE_T_BITS,ssize_t);
Should work for any value of SIZE_T_BITS, even something weird like 128 or 36 (PDP-10 port?), so long as you already have [u]intN_t defined.

It does require SIZE_T_BITS to be in bits rather than bytes, as your SIZEOF_SIZE_T is. But surely if your script can calculate SIZEOF_SIZE_T, it can multiply the answer by 8? (Or by CHAR_BIT, if we want to support weird platforms without 8-bit bytes – Lars Brinkhoff's PDP-10 port of GCC 3.2 has CHAR_BIT==9.)

Re: How SerenityOS declares ssize_t

#35
post #20

isn't magical development where you do things just because it "works for me" building technical debt?

Technical debt is like corporate debt, it's debt that you leverage to get to the market faster and make more money.

You may, or may not, have to pay it back.

So some technical debt is good in many cases as long as it is properly managed.

If you have zero debt it means you are not efficient (the same in finance or if you purchase a home cash while you could get a low and fixed interest rate loan for example).

Re: How SerenityOS declares ssize_t

#36
post #13
post #7

Earlier quoted context omitted.

What actual system is currently like this?

I'm sure they're out there. The closest that comes to mind is the MSP430, but not quite -- although it has 20 bit pointers (with sizeof ptr being... 4, since they're padded to 4 bytes for storage) and has a 16 bit size_t, my recollection is that ptrdiff_t is also defined at 16 bits (which I think violates the C spec, which requires at least 17 bits?). I haven't worked with many other segmented architectures recently,…

> my recollection is that ptrdiff_t is also defined at 16 bits (which I think violates the C spec, which requires at least 17 bits?)

C2x applies a proposal to permit pre-C99 limits:

  N2808    Allow 16-bit ptrdiff_t
N2808: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2808.htm

Draft C2x: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3047.pdf

Re: How SerenityOS declares ssize_t

#37

I would do it like this: #if SIZEOF_SIZE_T == 8 typedef int64_t ssize_t #elif SIZEOF_SIZE_T == 4 typedef int32_t ssize_t #elif SIZEOF_SIZE_T == 2 // LOL typedef int16_t ssize_t #else #error port me! #endif SIZEOF_SIZE_T can be obtained using a script which compiles a test program without executing it. Over they years I used more than one approach, settling on this one: https://www.kylheku.com/cgit/txr/tree/configure?…

Rather than a bunch of #if blocks, one could instead do: #define SIZE_T_BITS 64 #define PASTE3(a,b,c) a##b##c #define TYPEDEF_UINT(bits,name) typedef PASTE3(uint,bits,_t) name #define TYPEDEF_INT(bits,name) typedef PASTE3(int,bits,_t) name TYPEDEF_UINT(SIZE_T_BITS,size_t); TYPEDEF_INT(SIZE_T_BITS,ssize_t); Should work for any value of SIZE_T_BITS, even something weird like 128 or 36 (PDP-10 port?), so long as you alr…

The main thing in my post is how we get that input value, in your case SIZE_T_BITS, at the preprocessing level where we can then use it to select alternative pieces of code. After that it's cosmetic.

The simple, dumb #if blocks have the virtue is that they are not hostile to simple tooling, like Exuberant Ctags, Cscope, and whatnot. When we ask the editor to jump to the definition of ssize_t, it knows the three possible places where it is defined and serves them up.

This alternative is possible:

    #define PASTE3(a,b,c) a##b##c
    #define UINT_TYPE(bits) PASTE3(uint,bits,_t)
    #define INT_TYPE(bits,name) PASTE3(int,bits,_t)
    
    typedef UINT_TYPE(SIZE_T_BITS) usize_t;
    typedef INT_TYPE(SIZE_T_BITS) ssize_t;
I think in this form, there is a good chance the tools will grok the typedefs and index them, because we have not disguised the basic phrase structure.

Re: How SerenityOS declares ssize_t

#38
post #13

Earlier quoted context omitted.

I'm sure they're out there. The closest that comes to mind is the MSP430, but not quite -- although it has 20 bit pointers (with sizeof ptr being... 4, since they're padded to 4 bytes for storage) and has a 16 bit size_t, my recollection is that ptrdiff_t is also defined at 16 bits (which I think violates the C spec, which requires at least 17 bits?). I haven't worked with many other segmented architectures recently,…

The entire purpose of my question was that "I'm sure they're out there" is as close as I've ever found an answer to this, so I was looking for an actual example, not a hypothetical one.

IBM i (formerly known as AS/400) has two types of pointers, fat 128-bit pointers and thin pointers (which are either 32-bit or 64-bit, depending on the addressing mode). The 128-bit pointers contain embedded information on the type of the object they point to, and security capabilities for accessing it – there are actually several different types of fat pointers, which constrain which type of object they point to, but there is a generic pointer type ("open pointer") which can contain any other type of pointer, and hence point to anything. By contrast, the thin pointers are just memory addresses. IBM's C compiler defines extension keywords to declare if a given pointer type is fat or thin. However, they chose to define size_t, ptrdiff_t, etc, in terms of the thin pointers only. So, even this isn't a case of what you are looking for. But, if IBM had made some slightly different choices (permitted by the standard) in the design of their C compiler, it would have been. Also, back in the late 1980s / early 1990s there was at least one third party C compiler for the AS/400 (and its System/38 predecessor), and I'm not sure what choices that compiler made.

If people are looking for examples, I'm wondering about C compilers for Burroughs Large Systems. Or C compilers for Lisp machines (Symbolics had one). Those are the kind of weird architectures on which you'd do this, if anyone ever did. Indeed, it is rather obvious that the C standards committee gave compiler developers these unusual options with those weird architectures in mind. But it can't force them to make use of them, even if they are on a platform in which they might make sense.

Re: How SerenityOS declares ssize_t

#39
post #35
post #20

isn't magical development where you do things just because it "works for me" building technical debt?

Technical debt is like corporate debt, it's debt that you leverage to get to the market faster and make more money. You may, or may not, have to pay it back. So some technical debt is good in many cases as long as it is properly managed. If you have zero debt it means you are not efficient (the same in finance or if you purchase a home cash while you could get a low and fixed interest rate loan for example).

I've never thought of it this way before. Great point and analogy!

Re: How SerenityOS declares ssize_t

#40
Haha funny I came here hoping that someone had found a better solution than me, but you did the same thing. I think __SIZE_TYPE__ is not standard but a gcc extension (that clang also supports). I don't care because my target platform is Linux, but you might.

I have doubts about the legality of this solution though. The user might have #defined unsigned and this would break that. So far none of my users was mad enough to do it but I think they would be in their rights if they did.

If you only support "standard" platforms you can just typedef signed long ssize_t However some platforms (looking at you here, Windows!) will define long as 32-bit even on 64-bit and for those that will break. Not sure if __SIZE_TYPE__ is intrinsically declared on Windows in the first place. The C standard allows platforms where pointers have more bits than integers, in which case long would not work.

Hey, I just had an epiphany. You could use __PTRDIFF_SIZE__!

Post reply on HN