C++ Logo


Advanced search

Re: [std-proposals] Slow bulky integer types (128-bit)

From: Alejandro Colomar <alx.manpages_at_[hidden]>
Date: Thu, 30 Mar 2023 14:15:45 +0200
Hi Marcin, Jonathan,

On 3/30/23 12:58, Marcin Jaczewski wrote:
> czw., 30 mar 2023 o 12:36 Alejandro Colomar
> <alx.mailinglists_at_[hidden]> napisał(a):
>> On Thu, Mar 30, 2023, 12:27 Marcin Jaczewski via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>> czw., 30 mar 2023 o 11:26 Alejandro Colomar via Std-Proposals
>>> <std-proposals_at_[hidden]> napisał(a):
>>>> On 3/30/23 10:58, David Brown via Std-Proposals wrote:
>>>>> On 29/03/2023 17:18, Arthur O'Dwyer via Std-Proposals wrote:
>>>>>> A new `uintmax_extended_t` (or whatever) can communicate properly from
>>>>>> the get-go: "Hey! This type will change in the future! Don't build it
>>>>>> into your APIs!"
>>>>>> But then, if you aren't using this type in APIs, then where /*are*/ you
>>>>>> using it, and why does it need to exist in the standard library at all?
>>>>> That, I think, is the key point - /why/ would you want a "maximum size
>>>>> integer type" ?
>>>> For example to printf(3) an off_t variable.
>>>> Or to write a [[gnu::always_inline]] function that accepts any integer.
>>>> Or to write a type-generic macro that handles any integer.
>>>> The addition of functions that handle [u]intmax_t was the design mistake.
>>>> If they had been added as macros, we wouldn't be discussing ABI issues,
>>>> because macros don't have ABI. Of course, the problem is that _Generic(3)
>>>> was only added in C11, but intmax_t was added in C99, so they had to do it
>>>> as functions. History sucks.
>>> No, this would change nothing, if it was macro, you still could put it
>>> on some API and then worry about ABI break.
>>> Usage of this causes risk of ABI break, not how it was defined.
>> I already mentioned this on this thread (but maybe I sent the mail from an incorrect account and didn't reach the list): In the Linux man-pages page for _Generic(3), I wrote a proof of concept of how to implement imaxabs(3) and similar calls as macros, so that no traces of
>> intmax_t are left in any ABI:
>> <https://manpages.debian.org/testing/manpages-dev/_Generic.3.en.html>
>> The actual calls will be made to functions of fundamental types.
>> That would require modifying the standard to allow these calls to be implemented as macros only, with no real function of that name, but I think that shouldn't be a big deal. Implementations could keep a compat symbol for previous programs that called the function.
> But ABI break can happen in user code too. If you have a lot of code
> compiled by the old version, it could depend on `INTMAX_C`.
> When you try to link the same code but compiled with a new compiler
> you could have different behavior.

Yes, that can happen. But that's the user's fault. The point is that
not only libc is restricted from using intmax_t in ABIs, but also users
should be restricted.

If you pass intmax_t through a function parameter, you're creating an
ABI in user code, and intmax_t should never be used to create an ABI.

intmax_t is only useful for _converting_ local variables to the _current_
intmax_t, and pass it to a function of the appropriate size.

> Like:
> ```
> //shared definitions
> void write_long(void* v)
> {
> *((long*)v) = 1;
> }
> void write_long_long(void* v)
> {
> *((long long*)v) = 1;
> }
> void write_long_long_long(void* v)
> {
> *((long long long*)v) = 1;
> }
> #define write _Generic(INTMAX_C(0), \
> long: write_long, \
> long long: write_long_long \
> long long long: write_long_long_long \
> )
> //new code in one object file
> void foo(void* v)
> {
> write(v);
> }
> //old code in another object file
> void bar()
> {
> char p[sizeof(INTMAX_C(0))];
> foo(p); //UB! buffer overrun
> }

If you really need an array of intmax_t, make sure you recompile your
entire program all the time. But the advice would be to never use the
type in variables that are used across translation units, or in function
parameters. If you follow that, you should be safe.

Jonathan, I know pure C++ can live without these types or macros, and
C++ doesn't really need this type so much, but C was mentioned in this
thread (basically because this comes from C), so I answered on the
reasons why C still needs this type. C++ code that needs to be
compatible with C might also be interested in using it, but I didn't
write C++ code in the last few years, and am losing interest in making
my C code be C++-compatible, so I'm not the best one to talk about it.

In C, macros are fine. We don't have namespaces, so that's a non-issue,
and _Generic(3) is quite powerful, to the point that I don't miss
templates. always_inline is not so interesting to me (I tend to prefer
C99 inline), but it's useful in some cases, when you want to write a
function, but you want it to not set ABI issues, like with intmax_t.

Other than that, I agree with you in that intmax_t is libc's widest type.
In fact, being libc's and not libc++'s type, it makes sense to discuss it
as a C feature, implemented with macros.

I still think that we might want to extend libc in the future to support
128-bit types, and having intmax_t be the reason that prohibits this
makes me think we should consider fixing it (or rather, the functions
that use it).


> ```
>>> --
>>> Std-Proposals mailing list
>>> Std-Proposals_at_[hidden]
>>> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2023-03-30 12:15:48